py2-kms upload

This commit is contained in:
Matteo ℱan 2018-02-28 23:25:00 +01:00 committed by GitHub
parent 1d038f22b5
commit 0974c3e33a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
19 changed files with 4155 additions and 0 deletions

710
py2-kms/aes.py Normal file
View file

@ -0,0 +1,710 @@
#!/usr/bin/env python
# aes.py: implements AES - Advanced Encryption Standard
# from the SlowAES project, http://code.google.com/p/slowaes/
#
# Copyright (c) 2008 Josh Davis ( http://www.josh-davis.org ),
# Alex Martelli ( http://www.aleax.it )
#
# Ported from C code written by Laurent Haan ( http://www.progressive-coding.com )
#
# Licensed under the Apache License, Version 2.0
# http://www.apache.org/licenses/
import os
import math
class AES(object):
'''AES funtions for a single block.
'''
# Very annoying code: all is for an object, but no state is kept!
# Should just be plain functions in a AES modlule.
#*py-kms*
v6 = False
# valid key sizes
keySize = dict(SIZE_128=16, SIZE_192=24, SIZE_256=32)
# Rijndael S-box
sbox = [0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67,
0x2b, 0xfe, 0xd7, 0xab, 0x76, 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59,
0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, 0xb7,
0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1,
0x71, 0xd8, 0x31, 0x15, 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05,
0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, 0x09, 0x83,
0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29,
0xe3, 0x2f, 0x84, 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b,
0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf, 0xd0, 0xef, 0xaa,
0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c,
0x9f, 0xa8, 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc,
0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, 0xcd, 0x0c, 0x13, 0xec,
0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19,
0x73, 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee,
0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb, 0xe0, 0x32, 0x3a, 0x0a, 0x49,
0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79,
0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4,
0xea, 0x65, 0x7a, 0xae, 0x08, 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6,
0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, 0x70,
0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9,
0x86, 0xc1, 0x1d, 0x9e, 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e,
0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, 0x8c, 0xa1,
0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0,
0x54, 0xbb, 0x16]
# Rijndael Inverted S-box
rsbox = [0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3,
0x9e, 0x81, 0xf3, 0xd7, 0xfb, 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f,
0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb, 0x54,
0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b,
0x42, 0xfa, 0xc3, 0x4e, 0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24,
0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25, 0x72, 0xf8,
0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d,
0x65, 0xb6, 0x92, 0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda,
0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84, 0x90, 0xd8, 0xab,
0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3,
0x45, 0x06, 0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1,
0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b, 0x3a, 0x91, 0x11, 0x41,
0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6,
0x73, 0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9,
0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e, 0x47, 0xf1, 0x1a, 0x71, 0x1d,
0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b,
0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0,
0xfe, 0x78, 0xcd, 0x5a, 0xf4, 0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07,
0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f, 0x60,
0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f,
0x93, 0xc9, 0x9c, 0xef, 0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5,
0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61, 0x17, 0x2b,
0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55,
0x21, 0x0c, 0x7d]
def getSBoxValue(self,num):
""" Retrieves a given S-Box Value. """
return self.sbox[num]
def getSBoxInvert(self,num):
""" Retrieves a given Inverted S-Box Value. """
return self.rsbox[num]
def rotate(self, word):
""" Rijndael's key schedule rotate operation.
Rotate a word eight bits to the left: eg, rotate(1d2c3a4f) == 2c3a4f1d
Word is an char list of size 4 (32 bits overall).
"""
return word[1:] + word[:1]
# Rijndael Rcon
Rcon = [0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36,
0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97,
0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72,
0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66,
0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04,
0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d,
0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3,
0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61,
0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a,
0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40,
0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc,
0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5,
0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a,
0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d,
0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c,
0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35,
0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4,
0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc,
0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08,
0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a,
0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d,
0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2,
0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74,
0xe8, 0xcb]
def getRconValue(self, num):
""" Retrieves a given Rcon Value. """
return self.Rcon[num]
def core(self, word, iteration):
""" Key schedule core."""
# rotate the 32-bit word 8 bits to the left
word = self.rotate(word)
# apply S-Box substitution on all 4 parts of the 32-bit word
for i in range(4):
word[i] = self.getSBoxValue(word[i])
# XOR the output of the rcon operation with i to the first part
# (leftmost) only
word[0] = word[0] ^ self.getRconValue(iteration)
return word
def expandKey(self, key, size, expandedKeySize):
"""Rijndael's key expansion.
Expands an 128,192,256 key into an 176,208,240 bytes key
expandedKey is a char list of large enough size,
key is the non-expanded key.
"""
# current expanded keySize, in bytes
currentSize = 0
rconIteration = 1
expandedKey = [0] * expandedKeySize
# set the 16, 24, 32 bytes of the expanded key to the input key
for j in range(size):
expandedKey[j] = key[j]
currentSize += size
while currentSize < expandedKeySize:
# assign the previous 4 bytes to the temporary value t
t = expandedKey[currentSize - 4:currentSize]
# every 16,24,32 bytes we apply the core schedule to t
# and increment rconIteration afterwards
if currentSize % size == 0:
t = self.core(t, rconIteration)
rconIteration += 1
# For 256-bit keys, we add an extra sbox to the calculation
if size == self.keySize["SIZE_256"] and ((currentSize % size) == 16):
for l in range(4):
t[l] = self.getSBoxValue(t[l])
# We XOR t with the four-byte block 16,24,32 bytes before the new
# expanded key. This becomes the next four bytes in the expanded key.
for m in range(4):
expandedKey[currentSize] = expandedKey[currentSize - size] ^ t[m]
currentSize += 1
return expandedKey
def addRoundKey(self, state, roundKey):
""" Adds (XORs) the round key to the state. """
for i in range(16):
state[i] ^= roundKey[i]
return state
def createRoundKey(self, expandedKey, roundKeyPointer):
""" Create a round key.
Creates a round key from the given expanded key and the
position within the expanded key.
"""
roundKey = [0] * 16
for i in range(4):
for j in range(4):
roundKey[j * 4 + i] = expandedKey[roundKeyPointer + i * 4 + j]
return roundKey
def galois_multiplication(self, a, b):
""" Galois multiplication of 8 bit characters a and b. """
p = 0
for counter in range(8):
if b & 1: p ^= a
hi_bit_set = a & 0x80
a <<= 1
# keep a 8 bit
a &= 0xFF
if hi_bit_set:
a ^= 0x1b
b >>= 1
return p
def subBytes(self, state, isInv):
""" Substitute all the values from the state with the value in the SBox
using the state value as index for the SBox.
"""
if isInv:
getter = self.getSBoxInvert
else:
getter = self.getSBoxValue
for i in range(16):
state[i] = getter(state[i])
return state
def shiftRows(self, state, isInv):
""" Iterate over the 4 rows and call shiftRow() with that row. """
for i in range(4):
state = self.shiftRow(state, i * 4, i, isInv)
return state
def shiftRow(self, state, statePointer, nbr, isInv):
""" Each iteration shifts the row to the left by 1. """
for i in range(nbr):
if isInv:
state[statePointer:statePointer + 4] = state[statePointer + 3:statePointer + 4] + \
state[statePointer:statePointer + 3]
else:
state[statePointer:statePointer + 4] = state[statePointer + 1:statePointer + 4] + \
state[statePointer:statePointer + 1]
return state
def mixColumns(self, state, isInv):
"""Galois multiplication of the 4x4 matrix. """
# iterate over the 4 columns
for i in range(4):
# construct one column by slicing over the 4 rows
column = state[i:i + 16:4]
# apply the mixColumn on one column
column = self.mixColumn(column, isInv)
# put the values back into the state
state[i:i + 16:4] = column
return state
def mixColumn(self, column, isInv):
""" Galois multiplication of 1 column of the 4x4 matrix. """
if isInv:
mult = [14, 9, 13, 11]
else:
mult = [2, 1, 1, 3]
cpy = list(column)
g = self.galois_multiplication
column[0] = g(cpy[0], mult[0]) ^ g(cpy[3], mult[1]) ^ \
g(cpy[2], mult[2]) ^ g(cpy[1], mult[3])
column[1] = g(cpy[1], mult[0]) ^ g(cpy[0], mult[1]) ^ \
g(cpy[3], mult[2]) ^ g(cpy[2], mult[3])
column[2] = g(cpy[2], mult[0]) ^ g(cpy[1], mult[1]) ^ \
g(cpy[0], mult[2]) ^ g(cpy[3], mult[3])
column[3] = g(cpy[3], mult[0]) ^ g(cpy[2], mult[1]) ^ \
g(cpy[1], mult[2]) ^ g(cpy[0], mult[3])
return column
def aes_round(self, state, roundKey, roundKms):
""" Applies the 4 operations of the forward round in sequence. """
state = self.subBytes(state, False)
state = self.shiftRows(state, False)
state = self.mixColumns(state, False)
#*py-kms*
if self.v6:
if roundKms == 4:
state[0] ^= 0x73
if roundKms == 6:
state[0] ^= 0x09
if roundKms == 8:
state[0] ^= 0xE4
state = self.addRoundKey(state, roundKey)
return state
def aes_invRound(self, state, roundKey, roundKms):
""" Applies the 4 operations of the inverse round in sequence. """
state = self.shiftRows(state, True)
state = self.subBytes(state, True)
state = self.addRoundKey(state, roundKey)
#*py-kms*
if self.v6:
if roundKms == 4:
state[0] ^= 0x73
if roundKms == 6:
state[0] ^= 0x09
if roundKms == 8:
state[0] ^= 0xE4
state = self.mixColumns(state, True)
return state
def aes_main(self, state, expandedKey, nbrRounds):
""" Perform the initial operations, the standard round, and the final
operations of the forward aes, creating a round key for each round.
"""
state = self.addRoundKey(state, self.createRoundKey(expandedKey, 0))
i = 1
while i < nbrRounds:
state = self.aes_round(state, self.createRoundKey(expandedKey, 16 * i), i)
i += 1
state = self.subBytes(state, False)
state = self.shiftRows(state, False)
state = self.addRoundKey(state, self.createRoundKey(expandedKey, 16 * nbrRounds))
return state
def aes_invMain(self, state, expandedKey, nbrRounds):
""" Perform the initial operations, the standard round, and the final
operations of the inverse aes, creating a round key for each round.
"""
state = self.addRoundKey(state, self.createRoundKey(expandedKey, 16 * nbrRounds))
i = nbrRounds - 1
while i > 0:
state = self.aes_invRound(state, self.createRoundKey(expandedKey, 16 * i), i)
i -= 1
state = self.shiftRows(state, True)
state = self.subBytes(state, True)
state = self.addRoundKey(state, self.createRoundKey(expandedKey, 0))
return state
def encrypt(self, iput, key, size):
""" Encrypts a 128 bit input block against the given key of size specified. """
output = [0] * 16
# the number of rounds
nbrRounds = 0
# the 128 bit block to encode
block = [0] * 16
# set the number of rounds
if size == self.keySize["SIZE_128"]:
nbrRounds = 10
elif size == self.keySize["SIZE_192"]:
nbrRounds = 12
elif size == self.keySize["SIZE_256"]:
nbrRounds = 14
# *py-kms* The KMS v4 parameters
elif size == 20:
nbrRounds = 11
else:
return None
# the expanded keySize
expandedKeySize = 16 * (nbrRounds + 1)
# Set the block values, for the block:
# a0,0 a0,1 a0,2 a0,3
# a1,0 a1,1 a1,2 a1,3
# a2,0 a2,1 a2,2 a2,3
# a3,0 a3,1 a3,2 a3,3
# the mapping order is a0,0 a1,0 a2,0 a3,0 a0,1 a1,1 ... a2,3 a3,3
#
# iterate over the columns and over the rows
for i in range(4):
for j in range(4):
block[i + j * 4] = iput[i * 4 + j]
# expand the key into an 176, 208, 240 bytes key
# the expanded key
expandedKey = self.expandKey(key, size, expandedKeySize)
# encrypt the block using the expandedKey
block = self.aes_main(block, expandedKey, nbrRounds)
# unmap the block again into the output
for k in range(4):
for l in range(4):
output[k * 4 + l] = block[k + l * 4]
return output
def decrypt(self, iput, key, size):
""" decrypts a 128 bit input block against the given key of size specified. """
output = [0] * 16
# the number of rounds
nbrRounds = 0
# the 128 bit block to decode
block = [0] * 16
# set the number of rounds
if size == self.keySize["SIZE_128"]:
nbrRounds = 10
elif size == self.keySize["SIZE_192"]:
nbrRounds = 12
elif size == self.keySize["SIZE_256"]:
nbrRounds = 14
#*py-kms* The KMS v4 parameters.
elif size == 20:
nbrRounds = 11
else:
return None
# the expanded keySize
expandedKeySize = 16 * (nbrRounds + 1)
# Set the block values, for the block:
# a0,0 a0,1 a0,2 a0,3
# a1,0 a1,1 a1,2 a1,3
# a2,0 a2,1 a2,2 a2,3
# a3,0 a3,1 a3,2 a3,3
# the mapping order is a0,0 a1,0 a2,0 a3,0 a0,1 a1,1 ... a2,3 a3,3
# iterate over the columns and the rows
for i in range(4):
for j in range(4):
block[i + j * 4] = iput[i * 4 + j]
# expand the key into an 176, 208, 240 bytes key
expandedKey = self.expandKey(key, size, expandedKeySize)
# decrypt the block using the expandedKey
block = self.aes_invMain(block, expandedKey, nbrRounds)
# unmap the block again into the output
for k in range(4):
for l in range(4):
output[k * 4 + l] = block[k + l * 4]
return output
class AESModeOfOperation( object ):
'''Handles AES with plaintext consistingof multiple blocks.
Choice of block encoding modes: OFT, CFB, CBC
'''
# Very annoying code: all is for an object, but no state is kept!
# Should just be plain functions in an AES_BlockMode module.
aes = AES()
# structure of supported modes of operation
modeOfOperation = dict(OFB=0, CFB=1, CBC=2)
# converts a 16 character string into a number array
def convertString(self, string, start, end, mode):
if end - start > 16:
end = start + 16
if mode == self.modeOfOperation["CBC"]:
ar = [0] * 16
else: ar = []
i = start
j = 0
while len(ar) < end - start:
ar.append(0)
while i < end:
ar[j] = ord(string[i])
j += 1
i += 1
return ar
def encrypt(self, stringIn, mode, key, size, IV):
""" Mode of Operation Encryption
stringIn - Input String
mode - mode of type modeOfOperation
hexKey - a hex key of the bit length size
size - the bit length of the key
hexIV - the 128 bit hex Initilization Vector
"""
if len(key) % size:
return None
if len(IV) % 16:
return None
# the AES input/output
plaintext = []
iput = [0] * 16
output = []
ciphertext = [0] * 16
# the output cipher string
cipherOut = []
# char firstRound
firstRound = True
if stringIn != None:
for j in range(int(math.ceil(float(len(stringIn))/16))):
start = j * 16
end = j * 16 + 16
if end > len(stringIn):
end = len(stringIn)
plaintext = self.convertString(stringIn, start, end, mode)
if mode == self.modeOfOperation["CFB"]:
if firstRound:
output = self.aes.encrypt(IV, key, size)
firstRound = False
else:
output = self.aes.encrypt(iput, key, size)
for i in range(16):
if len(plaintext) - 1 < i:
ciphertext[i] = 0 ^ output[i]
elif len(output) - 1 < i:
ciphertext[i] = plaintext[i] ^ 0
elif len(plaintext) - 1 < i and len(output) < i:
ciphertext[i] = 0 ^ 0
else:
ciphertext[i] = plaintext[i] ^ output[i]
for k in range(end - start):
cipherOut.append(ciphertext[k])
iput = ciphertext
elif mode == self.modeOfOperation["OFB"]:
if firstRound:
output = self.aes.encrypt(IV, key, size)
firstRound = False
else:
output = self.aes.encrypt(iput, key, size)
for i in range(16):
if len(plaintext) - 1 < i:
ciphertext[i] = 0 ^ output[i]
elif len(output) - 1 < i:
ciphertext[i] = plaintext[i] ^ 0
elif len(plaintext) - 1 < i and len(output) < i:
ciphertext[i] = 0 ^ 0
else:
ciphertext[i] = plaintext[i] ^ output[i]
for k in range(end - start):
cipherOut.append(ciphertext[k])
iput = output
elif mode == self.modeOfOperation["CBC"]:
for i in range(16):
if firstRound:
iput[i] = plaintext[i] ^ IV[i]
else:
iput[i] = plaintext[i] ^ ciphertext[i]
firstRound = False
ciphertext = self.aes.encrypt(iput, key, size)
# always 16 bytes because of the padding for CBC
for k in range(16):
cipherOut.append(ciphertext[k])
return mode, len(stringIn), cipherOut
def decrypt(self, cipherIn, originalsize, mode, key, size, IV):
""" Mode of Operation Decryption
cipherIn - Encrypted String
originalsize - The unencrypted string length - required for CBC
mode - mode of type modeOfOperation
key - a number array of the bit length size
size - the bit length of the key
IV - the 128 bit number array Initilization Vector
"""
if len(key) % size:
return None
if len(IV) % 16:
return None
# the AES input/output
ciphertext = []
iput = []
output = []
plaintext = [0] * 16
# the output plain text character list
chrOut = []
# char firstRound
firstRound = True
if cipherIn != None:
for j in range(int(math.ceil(float(len(cipherIn))/16))):
start = j * 16
end = j * 16 + 16
if end > len(cipherIn):
end = len(cipherIn)
ciphertext = cipherIn[start:end]
if mode == self.modeOfOperation["CFB"]:
if firstRound:
output = self.aes.encrypt(IV, key, size)
firstRound = False
else:
output = self.aes.encrypt(iput, key, size)
for i in range(16):
if len(output) - 1 < i:
plaintext[i] = 0 ^ ciphertext[i]
elif len(ciphertext) - 1 < i:
plaintext[i] = output[i] ^ 0
elif len(output) - 1 < i and len(ciphertext) < i:
plaintext[i] = 0 ^ 0
else:
plaintext[i] = output[i] ^ ciphertext[i]
for k in range(end - start):
chrOut.append(chr(plaintext[k]))
iput = ciphertext
elif mode == self.modeOfOperation["OFB"]:
if firstRound:
output = self.aes.encrypt(IV, key, size)
firstRound = False
else:
output = self.aes.encrypt(iput, key, size)
for i in range(16):
if len(output) - 1 < i:
plaintext[i] = 0 ^ ciphertext[i]
elif len(ciphertext) - 1 < i:
plaintext[i] = output[i] ^ 0
elif len(output) - 1 < i and len(ciphertext) < i:
plaintext[i] = 0 ^ 0
else:
plaintext[i] = output[i] ^ ciphertext[i]
for k in range(end - start):
chrOut.append(chr(plaintext[k]))
iput = output
elif mode == self.modeOfOperation["CBC"]:
output = self.aes.decrypt(ciphertext, key, size)
for i in range(16):
if firstRound:
plaintext[i] = IV[i] ^ output[i]
else:
plaintext[i] = iput[i] ^ output[i]
firstRound = False
if originalsize is not None and originalsize < end:
for k in range(originalsize - start):
chrOut.append(chr(plaintext[k]))
else:
for k in range(end - start):
chrOut.append(chr(plaintext[k]))
iput = ciphertext
return "".join(chrOut)
def append_PKCS7_padding(s):
""" Return s padded to a multiple of 16-bytes by PKCS7 padding. """
numpads = 16 - (len(s)%16)
return s + numpads*chr(numpads)
def strip_PKCS7_padding(s):
""" Return s stripped of PKCS7 padding. """
if len(s)%16 or not s:
raise ValueError("String of len %d can't be PCKS7-padded" % len(s))
numpads = ord(s[-1])
if numpads > 16:
raise ValueError("String ending with %r can't be PCKS7-padded" % s[-1])
return s[:-numpads]
def encryptData(key, data, mode=AESModeOfOperation.modeOfOperation["CBC"]):
""" Encrypt `data` using `key`
`key` should be a string of bytes.
returned cipher is a string of bytes prepended with the initialization vector.
"""
key = map(ord, key)
if mode == AESModeOfOperation.modeOfOperation["CBC"]:
data = append_PKCS7_padding(data)
keysize = len(key)
assert keysize in AES.keySize.values(), 'invalid key size: %s' % keysize
# create a new iv using random data
iv = [ord(i) for i in os.urandom(16)]
moo = AESModeOfOperation()
(mode, length, ciph) = moo.encrypt(data, mode, key, keysize, iv)
# With padding, the original length does not need to be known. It's a bad
# idea to store the original message length prepend the iv.
return ''.join(map(chr, iv)) + ''.join(map(chr, ciph))
def decryptData(key, data, mode=AESModeOfOperation.modeOfOperation["CBC"]):
""" Decrypt `data` using `key`
`key` should be a string of bytes.
`data` should have the initialization vector prepended as a string of ordinal values.
"""
key = map(ord, key)
keysize = len(key)
assert keysize in AES.keySize.values(), 'invalid key size: %s' % keysize
# iv is first 16 bytes
iv = map(ord, data[:16])
data = map(ord, data[16:])
moo = AESModeOfOperation()
decr = moo.decrypt(data, None, mode, key, keysize, iv)
if mode == AESModeOfOperation.modeOfOperation["CBC"]:
decr = strip_PKCS7_padding(decr)
return decr
def generateRandomKey(keysize):
""" Generates a key from random data of length `keysize`.
The returned key is a string of bytes.
"""
if keysize not in (16, 24, 32):
emsg = 'Invalid keysize, %s. Should be one of (16, 24, 32).'
raise ValueError, emsg % keysize
return os.urandom(keysize)
def testStr(cleartext, keysize=16, modeName = "CBC"):
""" Test with random key, choice of mode. """
print 'Random key test', 'Mode:', modeName
print 'cleartext:', cleartext
key = generateRandomKey(keysize)
print 'Key:', [ord(x) for x in key]
mode = AESModeOfOperation.modeOfOperation[modeName]
cipher = encryptData(key, cleartext, mode)
print 'Cipher:', [ord(x) for x in cipher]
decr = decryptData(key, cipher, mode)
print 'Decrypted:', decr
if __name__ == "__main__":
moo = AESModeOfOperation()
cleartext = "This is a test with several blocks!"
cipherkey = [143, 194, 34, 208, 145, 203, 230, 143, 177, 246, 97, 206, 145, 92, 255, 84]
iv = [103, 35, 148, 239, 76, 213, 47, 118, 255, 222, 123, 176, 106, 134, 98, 92]
mode, orig_len, ciph = moo.encrypt(cleartext, moo.modeOfOperation["CBC"],
cipherkey, moo.aes.keySize["SIZE_128"], iv)
print 'm=%s, ol=%s (%s), ciph=%s' % (mode, orig_len, len(cleartext), ciph)
decr = moo.decrypt(ciph, orig_len, mode, cipherkey, moo.aes.keySize["SIZE_128"], iv)
print decr
testStr(cleartext, 16, "CBC")

260
py2-kms/client.py Normal file
View file

@ -0,0 +1,260 @@
#!/usr/bin/env python
import argparse
import binascii
import datetime
import random
import socket
import string
import struct
import sys
import uuid
import logging
import os
import filetimes, rpcBind, rpcRequest
from dcerpc import MSRPCHeader, MSRPCBindNak, MSRPCRequestHeader, MSRPCRespHeader
from kmsBase import kmsBase, UUID
from kmsRequestV4 import kmsRequestV4
from kmsRequestV5 import kmsRequestV5
from kmsRequestV6 import kmsRequestV6
from rpcBase import rpcBase
from formatText import shell_message, justify
config = {}
def main():
parser = argparse.ArgumentParser()
parser.add_argument("ip", action="store", help='The IP address or hostname of the KMS server.', type=str)
parser.add_argument("port", nargs="?", action="store", default=1688,
help='The port the KMS service is listening on. The default is \"1688\".', type=int)
parser.add_argument("-m", "--mode", dest="mode",
choices=["WindowsVista","Windows7","Windows8","Windows81","Windows10","Office2010","Office2013","Office2016"], default="Windows81",
help='Use this flag to manually specify a Microsoft product for testing the server. The default is \"Windows81\".', type=str)
parser.add_argument("-c", "--cmid", dest="cmid", default=None,
help='Use this flag to manually specify a CMID to use. If no CMID is specified, a random CMID will be generated.', type=str)
parser.add_argument("-n", "--name", dest="machineName", default=None,
help='Use this flag to manually specify an ASCII machineName to use. If no machineName is specified,\
a random machineName will be generated.', 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__ )) + "/py2kms_client.log",
help='Use this flag to set an output Logfile. The default is \"pykms_client.log\".', type=str)
config.update(vars(parser.parse_args()))
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')
checkConfig()
config['call_id'] = 1
updateConfig()
s = socket.socket()
logging.info("Connecting to %s on port %d..." % (config['ip'], config['port']))
s.connect((config['ip'], config['port']))
logging.info("Connection successful !")
binder = rpcBind.handler(None, config)
RPC_Bind = str(binder.generateRequest())
logging.info("Sending RPC bind request...")
shell_message(nshell = [-1, 1])
s.send(RPC_Bind)
try:
shell_message(nshell = [-4, 7])
bindResponse = s.recv(1024)
except socket.error, e:
if e[0] == 104:
logging.error("Connection reset by peer. Exiting...")
sys.exit()
else:
raise
if bindResponse == '' or not bindResponse:
logging.error("No data received ! Exiting...")
sys.exit()
packetType = MSRPCHeader(bindResponse)['type']
if packetType == rpcBase.packetType['bindAck']:
logging.info("RPC bind acknowledged.")
shell_message(nshell = 8)
kmsRequest = createKmsRequest()
requester = rpcRequest.handler(kmsRequest, config)
s.send(str(requester.generateRequest()))
shell_message(nshell = [-1, 12])
response = s.recv(1024)
logging.debug("Response: \n%s\n" % justify(binascii.b2a_hex(response)))
shell_message(nshell = [-4, 20])
parsed = MSRPCRespHeader(response)
kmsData = readKmsResponse(parsed['pduData'], kmsRequest, config)
kmsResp = kmsData['response']
try:
hwid = kmsData['hwid']
except:
hwid = None
logging.info("KMS Host ePID: %s" % kmsResp['kmsEpid'].decode('utf-16le').encode('utf-8'))
if hwid is not None:
logging.info("KMS Host HWID: %s" % binascii.b2a_hex(hwid).upper())
logging.info("KMS Host Current Client Count: %s" % kmsResp['currentClientCount'])
logging.info("KMS VL Activation Interval: %s" % kmsResp['vLActivationInterval'])
logging.info("KMS VL Renewal Interval: %s" % kmsResp['vLRenewalInterval'])
shell_message(nshell = 21)
elif packetType == rpcBase.packetType['bindNak']:
logging.info(justify(MSRPCBindNak(bindResponse).dump(print_to_stdout = False)))
sys.exit()
else:
logging.critical("Something went wrong.")
sys.exit()
def checkConfig():
if config['cmid'] is not None:
try:
uuid.UUID(config['cmid'])
except:
logging.error("Bad CMID. Exiting...")
sys.exit()
if config['machineName'] is not None:
if len(config['machineName']) < 2 or len(config['machineName']) > 63:
logging.error("machineName must be between 2 and 63 characters in length.")
sys.exit()
def updateConfig():
if config['mode'] == 'WindowsVista':
config['RequiredClientCount'] = 25
config['KMSProtocolMajorVersion'] = 4
config['KMSProtocolMinorVersion'] = 0
config['KMSClientLicenseStatus'] = 2
config['KMSClientAppID'] = "55c92734-d682-4d71-983e-d6ec3f16059f"
config['KMSClientSkuID'] = "cfd8ff08-c0d7-452b-9f60-ef5c70c32094"
config['KMSClientKMSCountedID'] = "212a64dc-43b1-4d3d-a30c-2fc69d2095c6"
elif config['mode'] == 'Windows7':
config['RequiredClientCount'] = 25
config['KMSProtocolMajorVersion'] = 4
config['KMSProtocolMinorVersion'] = 0
config['KMSClientLicenseStatus'] = 2
config['KMSClientAppID'] = "55c92734-d682-4d71-983e-d6ec3f16059f"
config['KMSClientSkuID'] = "ae2ee509-1b34-41c0-acb7-6d4650168915"
config['KMSClientKMSCountedID'] = "7fde5219-fbfa-484a-82c9-34d1ad53e856"
elif config['mode'] == 'Windows8':
config['RequiredClientCount'] = 25
config['KMSProtocolMajorVersion'] = 5
config['KMSProtocolMinorVersion'] = 0
config['KMSClientLicenseStatus'] = 2
config['KMSClientAppID'] = "55c92734-d682-4d71-983e-d6ec3f16059f"
config['KMSClientSkuID'] = "458e1bec-837a-45f6-b9d5-925ed5d299de"
config['KMSClientKMSCountedID'] = "3c40b358-5948-45af-923b-53d21fcc7e79"
elif config['mode'] == 'Windows81':
config['RequiredClientCount'] = 25
config['KMSProtocolMajorVersion'] = 6
config['KMSProtocolMinorVersion'] = 0
config['KMSClientLicenseStatus'] = 2
config['KMSClientAppID'] = "55c92734-d682-4d71-983e-d6ec3f16059f"
config['KMSClientSkuID'] = "81671aaf-79d1-4eb1-b004-8cbbe173afea"
config['KMSClientKMSCountedID'] = "cb8fc780-2c05-495a-9710-85afffc904d7"
elif config['mode'] == 'Windows10':
config['RequiredClientCount'] = 25
config['KMSProtocolMajorVersion'] = 6
config['KMSProtocolMinorVersion'] = 0
config['KMSClientLicenseStatus'] = 2
config['KMSClientAppID'] = "55c92734-d682-4d71-983e-d6ec3f16059f"
config['KMSClientSkuID'] = "73111121-5638-40f6-bc11-f1d7b0d64300"
config['KMSClientKMSCountedID'] = "58e2134f-8e11-4d17-9cb2-91069c151148"
elif config['mode'] == 'Office2010':
config['RequiredClientCount'] = 5
config['KMSProtocolMajorVersion'] = 4
config['KMSProtocolMinorVersion'] = 0
config['KMSClientLicenseStatus'] = 2
config['KMSClientAppID'] = "59a52881-a989-479d-af46-f275c6370663"
config['KMSClientSkuID'] = "6f327760-8c5c-417c-9b61-836a98287e0c"
config['KMSClientKMSCountedID'] = "e85af946-2e25-47b7-83e1-bebcebeac611"
elif config['mode'] == 'Office2013':
config['RequiredClientCount'] = 5
config['KMSProtocolMajorVersion'] = 5
config['KMSProtocolMinorVersion'] = 0
config['KMSClientLicenseStatus'] = 2
config['KMSClientAppID'] = "0ff1ce15-a989-479d-af46-f275c6370663"
config['KMSClientSkuID'] = "b322da9c-a2e2-4058-9e4e-f59a6970bd69"
config['KMSClientKMSCountedID'] = "e6a6f1bf-9d40-40c3-aa9f-c77ba21578c0"
elif config['mode'] == 'Office2016':
config['RequiredClientCount'] = 5
config['KMSProtocolMajorVersion'] = 6
config['KMSProtocolMinorVersion'] = 0
config['KMSClientLicenseStatus'] = 2
config['KMSClientAppID'] = "0ff1ce15-a989-479d-af46-f275c6370663"
config['KMSClientSkuID'] = "d450596f-894d-49e0-966a-fd39ed4c4c64"
config['KMSClientKMSCountedID'] = "85b5f61b-320b-4be3-814a-b76b2bfafc82"
def createKmsRequestBase():
requestDict = kmsBase.kmsRequestStruct()
requestDict['versionMinor'] = config['KMSProtocolMinorVersion']
requestDict['versionMajor'] = config['KMSProtocolMajorVersion']
requestDict['isClientVm'] = 0
requestDict['licenseStatus'] = config['KMSClientLicenseStatus']
requestDict['graceTime'] = 43200
requestDict['applicationId'] = UUID(uuid.UUID(config['KMSClientAppID']).bytes_le)
requestDict['skuId'] = UUID(uuid.UUID(config['KMSClientSkuID']).bytes_le)
requestDict['kmsCountedId'] = UUID(uuid.UUID(config['KMSClientKMSCountedID']).bytes_le)
requestDict['clientMachineId'] = UUID(uuid.UUID(config['cmid']).bytes_le if (config['cmid'] is not None) else uuid.uuid4().bytes_le)
requestDict['previousClientMachineId'] = '\0' * 16 #requestDict['clientMachineId'] # I'm pretty sure this is supposed to be a null UUID.
requestDict['requiredClientCount'] = config['RequiredClientCount']
requestDict['requestTime'] = filetimes.dt_to_filetime(datetime.datetime.utcnow())
requestDict['machineName'] = (config['machineName'] if (config['machineName'] is not None) else ''.join(random.choice(string.letters + string.digits) for i in range(random.randint(2,63)))).encode('utf-16le')
requestDict['mnPad'] = '\0'.encode('utf-16le') * (63 - len(requestDict['machineName'].decode('utf-16le')))
# Debug Stuff
shell_message(nshell = 9)
logging.debug("Request Base Dictionary: \n%s\n" % justify(requestDict.dump(print_to_stdout = False)))
return requestDict
def createKmsRequest():
# Update the call ID
config['call_id'] += 1
# KMS Protocol Major Version
if config['KMSProtocolMajorVersion'] == 4:
handler = kmsRequestV4(None, config)
elif config['KMSProtocolMajorVersion'] == 5:
handler = kmsRequestV5(None, config)
elif config['KMSProtocolMajorVersion'] == 6:
handler = kmsRequestV6(None, config)
else:
return None
requestBase = createKmsRequestBase()
return handler.generateRequest(requestBase)
def readKmsResponse(data, request, config):
if config['KMSProtocolMajorVersion'] == 4:
logging.info("Received V4 response")
response = readKmsResponseV4(data, request)
elif config['KMSProtocolMajorVersion'] == 5:
logging.info("Received V5 response")
response = readKmsResponseV5(data)
elif config['KMSProtocolMajorVersion'] == 6:
logging.info("Received V6 response")
response = readKmsResponseV6(data)
else:
logging.info("Unhandled response version: %d.%d" % (config['KMSProtocolMajorVersion'], config['KMSProtocolMinorVersion']))
logging.info("I'm not even sure how this happened...")
return response
def readKmsResponseV4(data, request):
response = kmsRequestV4.ResponseV4(data)
hashed = kmsRequestV4(data, config).generateHash(bytearray(str(response['response'])))
logging.info("Response Hash has expected value: ", hashed == response['hash'])
return response
def readKmsResponseV5(data):
response = kmsRequestV5.ResponseV5(data)
decrypted = kmsRequestV5(data, config).decryptResponse(response)
return decrypted
def readKmsResponseV6(data):
response = kmsRequestV6.ResponseV5(data)
decrypted = kmsRequestV6(data, config).decryptResponse(response)
message = decrypted['message']
return message
if __name__ == "__main__":
main()

365
py2-kms/dcerpc.py Normal file
View file

@ -0,0 +1,365 @@
#!/usr/bin/env python
# Copyright (c) 2003-2012 CORE Security Technologies
#
# This software is provided under under a slightly modified version
# of the Apache Software License. See the accompanying LICENSE file
# for more information.
#
# $Id: dcerpc.py 917 2013-11-10 20:47:57Z bethus $
#
# Partial C706.pdf + [MS-RPCE] implementation
#
# ToDo:
# [ ] Take out all the security provider stuff out of here (e.g. RPC_C_AUTHN_WINNT)
# and put it elsewhere. This will make the coder cleaner and easier to add
# more SSP (e.g. NETLOGON)
#
from structure import Structure,pack,unpack
# MS/RPC Constants
MSRPC_REQUEST = 0x00
MSRPC_PING = 0x01
MSRPC_RESPONSE = 0x02
MSRPC_FAULT = 0x03
MSRPC_WORKING = 0x04
MSRPC_NOCALL = 0x05
MSRPC_REJECT = 0x06
MSRPC_ACK = 0x07
MSRPC_CL_CANCEL = 0x08
MSRPC_FACK = 0x09
MSRPC_CANCELACK = 0x0A
MSRPC_BIND = 0x0B
MSRPC_BINDACK = 0x0C
MSRPC_BINDNAK = 0x0D
MSRPC_ALTERCTX = 0x0E
MSRPC_ALTERCTX_R= 0x0F
MSRPC_AUTH3 = 0x10
MSRPC_SHUTDOWN = 0x11
MSRPC_CO_CANCEL = 0x12
MSRPC_ORPHANED = 0x13
# MS/RPC Packet Flags
MSRPC_FIRSTFRAG = 0x01
MSRPC_LASTFRAG = 0x02
# For PDU types bind, bind_ack, alter_context, and
# alter_context_resp, this flag MUST be interpreted as PFC_SUPPORT_HEADER_SIGN
MSRPC_SUPPORT_SIGN = 0x04
#For the
#remaining PDU types, this flag MUST be interpreted as PFC_PENDING_CANCEL.
MSRPC_PENDING_CANCEL= 0x04
MSRPC_NOTAFRAG = 0x04
MSRPC_RECRESPOND = 0x08
MSRPC_NOMULTIPLEX = 0x10
MSRPC_NOTFORIDEMP = 0x20
MSRPC_NOTFORBCAST = 0x40
MSRPC_NOUUID = 0x80
# Auth Types - Security Providers
RPC_C_AUTHN_NONE = 0x00
RPC_C_AUTHN_GSS_NEGOTIATE = 0x09
RPC_C_AUTHN_WINNT = 0x0A
RPC_C_AUTHN_GSS_SCHANNEL = 0x0E
RPC_C_AUTHN_GSS_KERBEROS = 0x10
RPC_C_AUTHN_NETLOGON = 0x44
RPC_C_AUTHN_DEFAULT = 0xFF
# Auth Levels
RPC_C_AUTHN_LEVEL_NONE = 1
RPC_C_AUTHN_LEVEL_CONNECT = 2
RPC_C_AUTHN_LEVEL_CALL = 3
RPC_C_AUTHN_LEVEL_PKT = 4
RPC_C_AUTHN_LEVEL_PKT_INTEGRITY = 5
RPC_C_AUTHN_LEVEL_PKT_PRIVACY = 6
#Reasons for rejection of a context element, included in bind_ack result reason
rpc_provider_reason = {
0 : 'reason_not_specified',
1 : 'abstract_syntax_not_supported',
2 : 'proposed_transfer_syntaxes_not_supported',
3 : 'local_limit_exceeded',
4 : 'protocol_version_not_specified',
8 : 'authentication_type_not_recognized',
9 : 'invalid_checksum'
}
MSRPC_CONT_RESULT_ACCEPT = 0
MSRPC_CONT_RESULT_USER_REJECT = 1
MSRPC_CONT_RESULT_PROV_REJECT = 2
#Results of a presentation context negotiation
rpc_cont_def_result = {
0 : 'acceptance',
1 : 'user_rejection',
2 : 'provider_rejection'
}
#status codes, references:
#http://msdn.microsoft.com/library/default.asp?url=/library/en-us/rpc/rpc/rpc_return_values.asp
#http://msdn.microsoft.com/library/default.asp?url=/library/en-us/randz/protocol/common_return_values.asp
#winerror.h
#http://www.opengroup.org/onlinepubs/9629399/apdxn.htm
rpc_status_codes = {
0x00000005L : 'rpc_s_access_denied',
0x00000008L : 'Authentication type not recognized',
0x000006D8L : 'rpc_fault_cant_perform',
0x000006C6L : 'rpc_x_invalid_bound', # the arrays bound are invalid
0x000006E4L : 'rpc_s_cannot_support: The requested operation is not supported.', # some operation is not supported
0x000006F7L : 'rpc_x_bad_stub_data', # the stub data is invalid, doesn't match with the IDL definition
0x1C010001L : 'nca_s_comm_failure', # unable to get response from server:
0x1C010002L : 'nca_s_op_rng_error', # bad operation number in call
0x1C010003L : 'nca_s_unk_if', # unknown interface
0x1C010006L : 'nca_s_wrong_boot_time', # client passed server wrong server boot time
0x1C010009L : 'nca_s_you_crashed', # a restarted server called back a client
0x1C01000BL : 'nca_s_proto_error', # someone messed up the protocol
0x1C010013L : 'nca_s_out_args_too_big ', # output args too big
0x1C010014L : 'nca_s_server_too_busy', # server is too busy to handle call
0x1C010015L : 'nca_s_fault_string_too_long', # string argument longer than declared max len
0x1C010017L : 'nca_s_unsupported_type ', # no implementation of generic operation for object
0x1C000001L : 'nca_s_fault_int_div_by_zero',
0x1C000002L : 'nca_s_fault_addr_error ',
0x1C000003L : 'nca_s_fault_fp_div_zero',
0x1C000004L : 'nca_s_fault_fp_underflow',
0x1C000005L : 'nca_s_fault_fp_overflow',
0x1C000006L : 'nca_s_fault_invalid_tag',
0x1C000007L : 'nca_s_fault_invalid_bound ',
0x1C000008L : 'nca_s_rpc_version_mismatch',
0x1C000009L : 'nca_s_unspec_reject ',
0x1C00000AL : 'nca_s_bad_actid',
0x1C00000BL : 'nca_s_who_are_you_failed',
0x1C00000CL : 'nca_s_manager_not_entered ',
0x1C00000DL : 'nca_s_fault_cancel',
0x1C00000EL : 'nca_s_fault_ill_inst',
0x1C00000FL : 'nca_s_fault_fp_error',
0x1C000010L : 'nca_s_fault_int_overflow',
0x1C000012L : 'nca_s_fault_unspec',
0x1C000013L : 'nca_s_fault_remote_comm_failure ',
0x1C000014L : 'nca_s_fault_pipe_empty ',
0x1C000015L : 'nca_s_fault_pipe_closed',
0x1C000016L : 'nca_s_fault_pipe_order ',
0x1C000017L : 'nca_s_fault_pipe_discipline',
0x1C000018L : 'nca_s_fault_pipe_comm_error',
0x1C000019L : 'nca_s_fault_pipe_memory',
0x1C00001AL : 'nca_s_fault_context_mismatch ',
0x1C00001BL : 'nca_s_fault_remote_no_memory ',
0x1C00001CL : 'nca_s_invalid_pres_context_id',
0x1C00001DL : 'nca_s_unsupported_authn_level',
0x1C00001FL : 'nca_s_invalid_checksum ',
0x1C000020L : 'nca_s_invalid_crc',
0x1C000021L : 'nca_s_fault_user_defined',
0x1C000022L : 'nca_s_fault_tx_open_failed',
0x1C000023L : 'nca_s_fault_codeset_conv_error',
0x1C000024L : 'nca_s_fault_object_not_found ',
0x1C000025L : 'nca_s_fault_no_client_stub'
}
class Exception(Exception):
pass
# Context Item
class CtxItem(Structure):
structure = (
('ContextID','<H=0'),
('TransItems','B=0'),
('Pad','B=0'),
('AbstractSyntax','20s=""'),
('TransferSyntax','20s=""'),
)
class CtxItemResult(Structure):
structure = (
('Result','<H=0'),
('Reason','<H=0'),
('TransferSyntax','20s=""'),
)
class SEC_TRAILER(Structure):
commonHdr = (
('auth_type', 'B=10'),
('auth_level','B=0'),
('auth_pad_len','B=0'),
('auth_rsvrd','B=0'),
('auth_ctx_id','<L=747920'),
)
class MSRPCHeader(Structure):
_SIZE = 16
commonHdr = (
('ver_major','B=5'), # 0
('ver_minor','B=0'), # 1
('type','B=0'), # 2
('flags','B=0'), # 3
('representation','<L=0x10'), # 4
('frag_len','<H=self._SIZE+len(pduData)+len(pad)+len(sec_trailer)+len(auth_data)'), # 8
('auth_len','<H=len(auth_data)'), # 10
('call_id','<L=1'), # 12 <-- Common up to here (including this)
)
structure = (
('dataLen','_-pduData','self["frag_len"]-self["auth_len"]-self._SIZE-(8 if self["auth_len"] > 0 else 0)'),
('pduData',':'),
('_pad', '_-pad','(4 - ((self._SIZE + len(self["pduData"])) & 3) & 3)'),
('pad', ':'),
('_sec_trailer', '_-sec_trailer', '8 if self["auth_len"] > 0 else 0'),
('sec_trailer',':'),
('auth_dataLen','_-auth_data','self["auth_len"]'),
('auth_data',':'),
)
def __init__(self, data = None, alignment = 0):
Structure.__init__(self,data, alignment)
if data is None:
self['ver_major'] = 5
self['ver_minor'] = 0
self['flags'] = MSRPC_FIRSTFRAG | MSRPC_LASTFRAG
self['type'] = MSRPC_REQUEST
self.__frag_len_set = 0
self['auth_len'] = 0
self['pduData'] = ''
self['auth_data'] = ''
self['sec_trailer'] = ''
self['pad'] = ''
def get_header_size(self):
return self._SIZE
def get_packet(self):
if self['auth_data'] != '':
self['auth_len'] = len(self['auth_data'])
# The sec_trailer structure MUST be 4-byte aligned with respect to
# the beginning of the PDU. Padding octets MUST be used to align the
# sec_trailer structure if its natural beginning is not already 4-byte aligned
##self['pad'] = '\xAA' * (4 - ((self._SIZE + len(self['pduData'])) & 3) & 3)
return self.getData()
class MSRPCRequestHeader(MSRPCHeader):
_SIZE = 24
commonHdr = MSRPCHeader.commonHdr + (
('alloc_hint','<L=0'), # 16
('ctx_id','<H=0'), # 20
('op_num','<H=0'), # 22
)
def __init__(self, data = None, alignment = 0):
MSRPCHeader.__init__(self, data, alignment)
if data is None:
self['type'] = MSRPC_REQUEST
self['ctx_id'] = 0
class MSRPCRespHeader(MSRPCHeader):
_SIZE = 24
commonHdr = MSRPCHeader.commonHdr + (
('alloc_hint','<L=0'), # 16
('ctx_id','<H=0'), # 20
('cancel_count','<B=0'), # 22
('padding','<B=0'), # 23
)
def __init__(self, aBuffer = None, alignment = 0):
MSRPCHeader.__init__(self, aBuffer, alignment)
if aBuffer is None:
self['type'] = MSRPC_RESPONSE
self['ctx_id'] = 0
class MSRPCBind(Structure):
_CTX_ITEM_LEN = len(CtxItem())
structure = (
('max_tfrag','<H=4280'),
('max_rfrag','<H=4280'),
('assoc_group','<L=0'),
('ctx_num','B=0'),
('Reserved','B=0'),
('Reserved2','<H=0'),
('_ctx_items', '_-ctx_items', 'self["ctx_num"]*self._CTX_ITEM_LEN'),
('ctx_items',':'),
)
def __init__(self, data = None, alignment = 0):
Structure.__init__(self, data, alignment)
if data is None:
self['max_tfrag'] = 4280
self['max_rfrag'] = 4280
self['assoc_group'] = 0
self['ctx_num'] = 1
self['ctx_items'] = ''
self.__ctx_items = []
def addCtxItem(self, item):
self.__ctx_items.append(item)
def getData(self):
self['ctx_num'] = len(self.__ctx_items)
for i in self.__ctx_items:
self['ctx_items'] += i.getData()
return Structure.getData(self)
class MSRPCBindAck(Structure):
_SIZE = 26 # Up to SecondaryAddr
_CTX_ITEM_LEN = len(CtxItemResult())
commonHdr = (
('ver_major','B=5'), # 0
('ver_minor','B=0'), # 1
('type','B=0'), # 2
('flags','B=0'), # 3
('representation','<L=0x10'), # 4
('frag_len','<H=0'), # 8
('auth_len','<H=0'), # 10
('call_id','<L=1'), # 12 <-- Common up to here (including this)
)
structure = (
('max_tfrag','<H=0'),
('max_rfrag','<H=0'),
('assoc_group','<L=0'),
('SecondaryAddrLen','<H&SecondaryAddr'),
('SecondaryAddr','z'), # Optional if SecondaryAddrLen == 0
('PadLen','_-Pad','(4-((self["SecondaryAddrLen"]+self._SIZE) % 4))%4'),
('Pad',':'),
('ctx_num','B=0'),
('Reserved','B=0'),
('Reserved2','<H=0'),
('_ctx_items','_-ctx_items','self["ctx_num"]*self._CTX_ITEM_LEN'),
('ctx_items',':'),
('_sec_trailer', '_-sec_trailer', '8 if self["auth_len"] > 0 else 0'),
('sec_trailer',':'),
('auth_dataLen','_-auth_data','self["auth_len"]'),
('auth_data',':'),
)
def __init__(self, data = None, alignment = 0):
self.__ctx_items = []
Structure.__init__(self,data,alignment)
if data is None:
self['Pad'] = ''
self['ctx_items'] = ''
self['sec_trailer'] = ''
self['auth_data'] = ''
def getCtxItems(self):
return self.__ctx_items
def getCtxItem(self,index):
return self.__ctx_items[index-1]
def fromString(self, data):
Structure.fromString(self,data)
# Parse the ctx_items
data = self['ctx_items']
for i in range(self['ctx_num']):
item = CtxItemResult(data)
self.__ctx_items.append(item)
data = data[len(item):]
class MSRPCBindNak(Structure):
structure = (
('RejectedReason','<H=0'),
('SupportedVersions',':'),
)
def __init__(self, data = None, alignment = 0):
Structure.__init__(self,data,alignment)
if data is None:
self['SupportedVersions'] = ''

106
py2-kms/filetimes.py Normal file
View file

@ -0,0 +1,106 @@
#!/usr/bin/env python
# Copyright (c) 2009, David Buxton <david@gasmark6.com>
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
# IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
# TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""Tools to convert between Python datetime instances and Microsoft times.
"""
from datetime import datetime, timedelta, tzinfo
from calendar import timegm
# http://support.microsoft.com/kb/167296
# How To Convert a UNIX time_t to a Win32 FILETIME or SYSTEMTIME
EPOCH_AS_FILETIME = 116444736000000000 # January 1, 1970 as MS file time
HUNDREDS_OF_NANOSECONDS = 10000000
ZERO = timedelta(0)
HOUR = timedelta(hours=1)
class UTC(tzinfo):
"""UTC"""
def utcoffset(self, dt):
return ZERO
def tzname(self, dt):
return "UTC"
def dst(self, dt):
return ZERO
utc = UTC()
def dt_to_filetime(dt):
"""Converts a datetime to Microsoft filetime format. If the object is
time zone-naive, it is forced to UTC before conversion.
>>> "%.0f" % dt_to_filetime(datetime(2009, 7, 25, 23, 0))
'128930364000000000'
>>> "%.0f" % dt_to_filetime(datetime(1970, 1, 1, 0, 0, tzinfo=utc))
'116444736000000000'
>>> "%.0f" % dt_to_filetime(datetime(1970, 1, 1, 0, 0))
'116444736000000000'
>>> dt_to_filetime(datetime(2009, 7, 25, 23, 0, 0, 100))
128930364000001000
"""
if (dt.tzinfo is None) or (dt.tzinfo.utcoffset(dt) is None):
dt = dt.replace(tzinfo=utc)
ft = EPOCH_AS_FILETIME + (timegm(dt.timetuple()) * HUNDREDS_OF_NANOSECONDS)
return ft + (dt.microsecond * 10)
def filetime_to_dt(ft):
"""Converts a Microsoft filetime number to a Python datetime. The new
datetime object is time zone-naive but is equivalent to tzinfo=utc.
>>> filetime_to_dt(116444736000000000)
datetime.datetime(1970, 1, 1, 0, 0)
>>> filetime_to_dt(128930364000000000)
datetime.datetime(2009, 7, 25, 23, 0)
>>> filetime_to_dt(128930364000001000)
datetime.datetime(2009, 7, 25, 23, 0, 0, 100)
"""
# Get seconds and remainder in terms of Unix epoch
(s, ns100) = divmod(ft - EPOCH_AS_FILETIME, HUNDREDS_OF_NANOSECONDS)
# Convert to datetime object
dt = datetime.utcfromtimestamp(s)
# Add remainder in as microseconds. Python 3.2 requires an integer
dt = dt.replace(microsecond=(ns100 // 10))
return dt
if __name__ == "__main__":
import doctest
doctest.testmod()

96
py2-kms/formatText.py Normal file
View file

@ -0,0 +1,96 @@
#!/usr/bin/env python
import sys
def justify(astring, indent = 35, break_every = 100):
str_indent = ('\n' + ' ' * indent)
splitted = astring.split('\n')
longests = [(n, s) for n, s in enumerate(splitted) if len(s) >= break_every]
for longest in longests:
lines = []
for i in range(0, len(longest[1]), break_every):
lines.append(longest[1][i : i + break_every])
splitted[longest[0]] = str_indent.join(lines)
if len(splitted) > 1:
justy = str_indent.join(splitted)
else:
justy = str_indent + str_indent.join(splitted)
return justy
class ShellStyle(object):
def style(self, s, style):
return style + s + '\033[0m'
def green(self, s):
return self.style(s, '\033[92m')
def blue(self, s):
return self.style(s, '\033[94m')
def yellow(self, s):
return self.style(s, '\033[93m')
def red(self, s):
return self.style(s, '\033[91m')
def magenta(self, s):
return self.style(s, '\033[95m')
def cyan(self, s):
return self.style(s, '\033[96m')
def white(self, s):
return self.style(s, '\033[97m')
def bold(self, s):
return self.style(s, '\033[1m')
def underline(self, s):
return self.style(s, '\033[4m')
def shell_message(nshell):
shelldict = {0: ShellStyle().yellow("Client generating RPC Bind Request..."),
1: ShellStyle().yellow("Client sending RPC Bind Request...") + ShellStyle().red("\t\t\t\t===============>"),
2: ShellStyle().red("===============>\t\t") + ShellStyle().yellow("Server received RPC Bind Request !!!"),
3: ShellStyle().yellow("\t\t\t\tServer parsing RPC Bind Request..."),
4: ShellStyle().yellow("\t\t\t\tServer generating RPC Bind Response..."),
5: ShellStyle().red("<===============\t\t") + ShellStyle().yellow("Server sending RPC Bind Response..."),
6: ShellStyle().green("\t\t\t\tRPC Bind acknowledged !!!\n"),
7: ShellStyle().yellow("Client received RPC Bind Response !!!") + ShellStyle().red("\t\t\t\t<==============="),
8: ShellStyle().green("RPC Bind acknowledged !!!\n"),
9: ShellStyle().blue("Client generating Activation Request dictionary..."),
10: ShellStyle().blue("Client generating Activation Request data..."),
11: ShellStyle().blue("Client generating RPC Activation Request..."),
12: ShellStyle().blue("Client sending RPC Activation Request...") + ShellStyle().red("\t\t\t===============>"),
13: ShellStyle().red("===============>\t\t") + ShellStyle().blue("Server received RPC Activation Request !!!"),
14: ShellStyle().blue("\t\t\t\tServer parsing RPC Activation Request..."),
15: ShellStyle().blue("\t\t\t\tServer processing KMS Activation Request..."),
16: ShellStyle().blue("\t\t\t\tServer processing KMS Activation Response..."),
17: ShellStyle().blue("\t\t\t\tServer generating RPC Activation Response..."),
18: ShellStyle().red("<===============\t\t") + ShellStyle().blue("Server sending RPC Activation Response..."),
19: ShellStyle().green("\t\t\t\tServer responded, now in Stand by...\n"),
20: ShellStyle().blue("Client received Response !!!") + ShellStyle().red("\t\t\t\t\t<==============="),
21: ShellStyle().green("Activation Done !!!"),
-1: ShellStyle().red("\t\t\t\t\t\t\t\tServer receiving"),
-2: ShellStyle().red("Client sending"),
-3: ShellStyle().red("Client receiving"),
-4: ShellStyle().red("\t\t\t\t\t\t\t\tServer sending")
}
if isinstance(nshell, list):
for n in nshell:
print shelldict[n]
else:
print shelldict[nshell]

628
py2-kms/kmsBase.py Normal file
View file

@ -0,0 +1,628 @@
#!/usr/bin/env python
import binascii
import logging
import datetime
import os
import struct
import sys
import time
import uuid
from structure import Structure
import kmsPidGenerator
import filetimes
from formatText import justify, shell_message
# sqlite3 is optional
try:
import sqlite3
except:
pass
class UUID(Structure):
commonHdr = ()
structure = (
('raw', '16s'),
)
def get(self):
return uuid.UUID(bytes_le=str(self))
class kmsBase:
class kmsRequestStruct(Structure):
commonHdr = ()
structure = (
('versionMinor', '<H'),
('versionMajor', '<H'),
('isClientVm', '<I'),
('licenseStatus', '<I'),
('graceTime', '<I'),
('applicationId', ':', UUID),
('skuId', ':', UUID),
('kmsCountedId' , ':', UUID),
('clientMachineId', ':', UUID),
('requiredClientCount', '<I'),
('requestTime', '<Q'),
('previousClientMachineId', ':', UUID),
('machineName', 'u'),
('_mnPad', '_-mnPad', '126-len(machineName)'),
('mnPad', ':'),
)
def getMachineName(self):
return self['machineName'].decode('utf-16le')
def getLicenseStatus(self):
return kmsBase.licenseStates[self['licenseStatus']] or "Unknown"
class kmsResponseStruct(Structure):
commonHdr = ()
structure = (
('versionMinor', '<H'),
('versionMajor', '<H'),
('epidLen', '<I=len(kmsEpid)+2'),
('kmsEpid', 'u'),
('clientMachineId', ':', UUID),
('responseTime', '<Q'),
('currentClientCount', '<I'),
('vLActivationInterval', '<I'),
('vLRenewalInterval', '<I'),
)
class GenericRequestHeader(Structure):
commonHdr = ()
structure = (
('bodyLength1', '<I'),
('bodyLength2', '<I'),
('versionMinor', '<H'),
('versionMajor', '<H'),
('remainder', '_'),
)
appIds = {
uuid.UUID("55C92734-D682-4D71-983E-D6EC3F16059F") : "Windows",
uuid.UUID("59A52881-A989-479D-AF46-F275C6370663") : "Office 14 (2010)",
uuid.UUID("0FF1CE15-A989-479D-AF46-F275C6370663") : "Office 15 (2013) / Office 16 (2016)"
}
skuIds = {
#########################
## Windows Server 2016 ##
#########################
uuid.UUID("21c56779-b449-4d20-adfc-eece0e1ad74b") : "Windows Server 2016 Datacenter",
uuid.UUID("8c1c5410-9f39-4805-8c9d-63a07706358f") : "Windows Server 2016 Standard",
uuid.UUID("2b5a1b0f-a5ab-4c54-ac2f-a6d94824a283") : "Windows Server 2016 Essentials",
uuid.UUID("7b4433f4-b1e7-4788-895a-c45378d38253") : "Windows Server 2016 Cloud Storage",
uuid.UUID("3dbf341b-5f6c-4fa7-b936-699dce9e263f") : "Windows Server 2016 Azure Core",
################
## Windows 10 ##
################
uuid.UUID("2de67392-b7a7-462a-b1ca-108dd189f588") : "Windows 10 Professional",
uuid.UUID("a80b5abf-76ad-428b-b05d-a47d2dffeebf") : "Windows 10 Professional N",
uuid.UUID("3f1afc82-f8ac-4f6c-8005-1d233e606eee") : "Windows 10 Professional Education",
uuid.UUID("5300b18c-2e33-4dc2-8291-47ffcec746dd") : "Windows 10 Professional Education N",
uuid.UUID("e0c42288-980c-4788-a014-c080d2e1926e") : "Windows 10 Education",
uuid.UUID("3c102355-d027-42c6-ad23-2e7ef8a02585") : "Windows 10 Education N",
uuid.UUID("73111121-5638-40f6-bc11-f1d7b0d64300") : "Windows 10 Enterprise",
uuid.UUID("e272e3e2-732f-4c65-a8f0-484747d0d947") : "Windows 10 Enterprise N",
uuid.UUID("7b51a46c-0c04-4e8f-9af4-8496cca90d5e") : "Windows 10 Enterprise 2015 LTSB",
uuid.UUID("87b838b7-41b6-4590-8318-5797951d8529") : "Windows 10 Enterprise 2015 LTSB N",
uuid.UUID("2d5a5a60-3040-48bf-beb0-fcd770c20ce0") : "Windows 10 Enterprise 2016 LTSB",
uuid.UUID("9f776d83-7156-45b2-8a5c-359b9c9f22a3") : "Windows 10 Enterprise 2016 LTSB N",
uuid.UUID("58e97c99-f377-4ef1-81d5-4ad5522b5fd8") : "Windows 10 Home / Core",
uuid.UUID("7b9e1751-a8da-4f75-9560-5fadfe3d8e38") : "Windows 10 Home / Core N",
uuid.UUID("cd918a57-a41b-4c82-8dce-1a538e221a83") : "Windows 10 Home / Core Single Language",
uuid.UUID("a9107544-f4a0-4053-a96a-1479abdef912") : "Windows 10 Home / Core Country Specific",
############################
## Windows Server 2012 R2 ##
############################
uuid.UUID("b3ca044e-a358-4d68-9883-aaa2941aca99") : "Windows Server 2012 R2 Standard",
uuid.UUID("00091344-1ea4-4f37-b789-01750ba6988c") : "Windows Server 2012 R2 Datacenter",
uuid.UUID("21db6ba4-9a7b-4a14-9e29-64a60c59301d") : "Windows Server 2012 R2 Essentials",
uuid.UUID("b743a2be-68d4-4dd3-af32-92425b7bb623") : "Windows Server 2012 R2 Cloud Storage",
#################
## Windows 8.1 ##
#################
uuid.UUID("c06b6981-d7fd-4a35-b7b4-054742b7af67") : "Windows 8.1 Professional",
uuid.UUID("7476d79f-8e48-49b4-ab63-4d0b813a16e4") : "Windows 8.1 Professional N",
uuid.UUID("096ce63d-4fac-48a9-82a9-61ae9e800e5f") : "Windows 8.1 Professional WMC",
uuid.UUID("81671aaf-79d1-4eb1-b004-8cbbe173afea") : "Windows 8.1 Enterprise",
uuid.UUID("113e705c-fa49-48a4-beea-7dd879b46b14") : "Windows 8.1 Enterprise N",
uuid.UUID("f7e88590-dfc7-4c78-bccb-6f3865b99d1a") : "Windows 8.1 Embedded Industry Automotive",
uuid.UUID("cd4e2d9f-5059-4a50-a92d-05d5bb1267c7") : "Windows 8.1 Embedded Industry Enterprise",
uuid.UUID("0ab82d54-47f4-4acb-818c-cc5bf0ecb649") : "Windows 8.1 Embedded Industry Professional",
uuid.UUID("fe1c3238-432a-43a1-8e25-97e7d1ef10f3") : "Windows 8.1 Core",
uuid.UUID("78558a64-dc19-43fe-a0d0-8075b2a370a3") : "Windows 8.1 Core N",
uuid.UUID("c72c6a1d-f252-4e7e-bdd1-3fca342acb35") : "Windows 8.1 Core Single Language",
uuid.UUID("db78b74f-ef1c-4892-abfe-1e66b8231df6") : "Windows 8.1 Core Country Specific",
uuid.UUID("ffee456a-cd87-4390-8e07-16146c672fd0") : "Windows 8.1 Core ARM",
uuid.UUID("e9942b32-2e55-4197-b0bd-5ff58cba8860") : "Windows 8.1 Core Connected",
uuid.UUID("c6ddecd6-2354-4c19-909b-306a3058484e") : "Windows 8.1 Core Connected N",
uuid.UUID("ba998212-460a-44db-bfb5-71bf09d1c68b") : "Windows 8.1 Core Connected Country Specific",
uuid.UUID("b8f5e3a3-ed33-4608-81e1-37d6c9dcfd9c") : "Windows 8.1 Core Connected Single Language",
uuid.UUID("e58d87b5-8126-4580-80fb-861b22f79296") : "Windows 8.1 Professional Student",
uuid.UUID("cab491c7-a918-4f60-b502-dab75e334f40") : "Windows 8.1 Professional Student N",
#########################
## Windows Server 2012 ##
#########################
uuid.UUID("c04ed6bf-55c8-4b47-9f8e-5a1f31ceee60") : "Windows Server 2012 / Windows 8 Core",
uuid.UUID("197390a0-65f6-4a95-bdc4-55d58a3b0253") : "Windows Server 2012 N / Windows 8 Core N",
uuid.UUID("8860fcd4-a77b-4a20-9045-a150ff11d609") : "Windows Server 2012 Single Language / Windows 8 Core Single Language",
uuid.UUID("9d5584a2-2d85-419a-982c-a00888bb9ddf") : "Windows Server 2012 Country Specific / Windows 8 Core Country Specific",
uuid.UUID("f0f5ec41-0d55-4732-af02-440a44a3cf0f") : "Windows Server 2012 Standard",
uuid.UUID("7d5486c7-e120-4771-b7f1-7b56c6d3170c") : "Windows Server 2012 MultiPoint Standard",
uuid.UUID("95fd1c83-7df5-494a-be8b-1300e1c9d1cd") : "Windows Server 2012 MultiPoint Premium",
uuid.UUID("d3643d60-0c42-412d-a7d6-52e6635327f6") : "Windows Server 2012 Datacenter",
#########################
## Windows Server 2010 ##
#########################
uuid.UUID("f772515c-0e87-48d5-a676-e6962c3e1195") : "Windows MultiPoint Server 2010",
###############
## Windows 8 ##
###############
uuid.UUID("a98bcd6d-5343-4603-8afe-5908e4611112") : "Windows 8 Professional",
uuid.UUID("ebf245c1-29a8-4daf-9cb1-38dfc608a8c8") : "Windows 8 Professional N",
uuid.UUID("a00018a3-f20f-4632-bf7c-8daa5351c914") : "Windows 8 Professional WMC",
uuid.UUID("458e1bec-837a-45f6-b9d5-925ed5d299de") : "Windows 8 Enterprise",
uuid.UUID("e14997e7-800a-4cf7-ad10-de4b45b578db") : "Windows 8 Enterprise N",
uuid.UUID("10018baf-ce21-4060-80bd-47fe74ed4dab") : "Windows 8 Embedded Industry Professional",
uuid.UUID("18db1848-12e0-4167-b9d7-da7fcda507db") : "Windows 8 Embedded Industry Enterprise",
uuid.UUID("af35d7b7-5035-4b63-8972-f0b747b9f4dc") : "Windows 8 Core ARM",
############################
## Windows Server 2008 R2 ##
############################
uuid.UUID("a78b8bd9-8017-4df5-b86a-09f756affa7c") : "Windows Server 2008 R2 Web",
uuid.UUID("cda18cf3-c196-46ad-b289-60c072869994") : "Windows Server 2008 R2 HPC Edition (Compute Cluster)",
uuid.UUID("68531fb9-5511-4989-97be-d11a0f55633f") : "Windows Server 2008 R2 Standard",
uuid.UUID("620e2b3d-09e7-42fd-802a-17a13652fe7a") : "Windows Server 2008 R2 Enterprise",
uuid.UUID("7482e61b-c589-4b7f-8ecc-46d455ac3b87") : "Windows Server 2008 R2 Datacenter",
uuid.UUID("8a26851c-1c7e-48d3-a687-fbca9b9ac16b") : "Windows Server 2008 R2 for Itanium-based Systems",
###############
## Windows 7 ##
###############
uuid.UUID("b92e9980-b9d5-4821-9c94-140f632f6312") : "Windows 7 Professional",
uuid.UUID("54a09a0d-d57b-4c10-8b69-a842d6590ad5") : "Windows 7 Professional N",
uuid.UUID("5a041529-fef8-4d07-b06f-b59b573b32d2") : "Windows 7 Professional E",
uuid.UUID("ae2ee509-1b34-41c0-acb7-6d4650168915") : "Windows 7 Enterprise",
uuid.UUID("1cb6d605-11b3-4e14-bb30-da91c8e3983a") : "Windows 7 Enterprise N",
uuid.UUID("46bbed08-9c7b-48fc-a614-95250573f4ea") : "Windows 7 Enterprise E",
uuid.UUID("db537896-376f-48ae-a492-53d0547773d0") : "Windows 7 Embedded POSReady",
uuid.UUID("aa6dd3aa-c2b4-40e2-a544-a6bbb3f5c395") : "Windows 7 Embedded ThinPC",
uuid.UUID("e1a8296a-db37-44d1-8cce-7bc961d59c54") : "Windows 7 Embedded Standard",
#########################
## Windows Server 2008 ##
#########################
uuid.UUID("ddfa9f7c-f09e-40b9-8c1a-be877a9a7f4b") : "Windows Server 2008 Web",
uuid.UUID("ad2542d4-9154-4c6d-8a44-30f11ee96989") : "Windows Server 2008 Standard",
uuid.UUID("2401e3d0-c50a-4b58-87b2-7e794b7d2607") : "Windows Server 2008 Standard without Hyper-V",
uuid.UUID("c1af4d90-d1bc-44ca-85d4-003ba33db3b9") : "Windows Server 2008 Enterprise",
uuid.UUID("8198490a-add0-47b2-b3ba-316b12d647b4") : "Windows Server 2008 Enterprise without Hyper-V",
uuid.UUID("7afb1156-2c1d-40fc-b260-aab7442b62fe") : "Windows Server 2008 HPC Edition (Compute Cluster)",
uuid.UUID("68b6e220-cf09-466b-92d3-45cd964b9509") : "Windows Server 2008 Datacenter",
uuid.UUID("fd09ef77-5647-4eff-809c-af2b64659a45") : "Windows Server 2008 Datacenter without Hyper-V",
uuid.UUID("01ef176b-3e0d-422a-b4f8-4ea880035e8f") : "Windows Server 2008 for Itanium-based Systems",
###################
## Windows Vista ##
###################
uuid.UUID("4f3d1606-3fea-4c01-be3c-8d671c401e3b") : "Windows Vista Business",
uuid.UUID("2c682dc2-8b68-4f63-a165-ae291d4cf138") : "Windows Vista Business N",
uuid.UUID("cfd8ff08-c0d7-452b-9f60-ef5c70c32094") : "Windows Vista Enterprise",
uuid.UUID("d4f54950-26f2-4fb4-ba21-ffab16afcade") : "Windows Vista Enterprise N",
#################
## Office 2016 ##
#################
uuid.UUID("d450596f-894d-49e0-966a-fd39ed4c4c64") : "Office Professional Plus 2016",
uuid.UUID("dedfa23d-6ed1-45a6-85dc-63cae0546de6") : "Office Standard 2016",
uuid.UUID("4f414197-0fc2-4c01-b68a-86cbb9ac254c") : "Office Project Professional 2016",
uuid.UUID("829b8110-0e6f-4349-bca4-42803577788d") : "Office Project Professional 2016 [Click-to-Run]",
uuid.UUID("da7ddabc-3fbe-4447-9e01-6ab7440b4cd4") : "Office Project Standard 2016",
uuid.UUID("cbbaca45-556a-4416-ad03-bda598eaa7c8") : "Office Project Standard 2016 [Click-to-Run]",
uuid.UUID("6bf301c1-b94a-43e9-ba31-d494598c47fb") : "Office Visio Professional 2016",
uuid.UUID("b234abe3-0857-4f9c-b05a-4dc314f85557") : "Office Visio Professional 2016 [Click-to-Run]",
uuid.UUID("aa2a7821-1827-4c2c-8f1d-4513a34dda97") : "Office Visio Standard 2016",
uuid.UUID("361fe620-64f4-41b5-ba77-84f8e079b1f7") : "Office Visio Standard 2016 [Click-to-Run]",
uuid.UUID("67c0fc0c-deba-401b-bf8b-9c8ad8395804") : "Office Access 2016",
uuid.UUID("c3e65d36-141f-4d2f-a303-a842ee756a29") : "Office Excel 2016",
uuid.UUID("9caabccb-61b1-4b4b-8bec-d10a3c3ac2ce") : "Office Mondo 2016",
uuid.UUID("e914ea6e-a5fa-4439-a394-a9bb3293ca09") : "Office Mondo Retail 2016",
uuid.UUID("d8cace59-33d2-4ac7-9b1b-9b72339c51c8") : "Office OneNote 2016",
uuid.UUID("ec9d9265-9d1e-4ed0-838a-cdc20f2551a1") : "Office Outlook 2016",
uuid.UUID("d70b1bba-b893-4544-96e2-b7a318091c33") : "Office Powerpoint 2016",
uuid.UUID("041a06cb-c5b8-4772-809f-416d03d16654") : "Office Publisher 2016",
uuid.UUID("83e04ee1-fa8d-436d-8994-d31a862cab77") : "Office Skype for Business 2016",
uuid.UUID("bb11badf-d8aa-470e-9311-20eaf80fe5cc") : "Office Word 2016",
#################
## Office 2013 ##
#################
uuid.UUID("87d2b5bf-d47b-41fb-af62-71c382f5cc85") : "Office Professional Plus 2013 [Preview]",
uuid.UUID("b322da9c-a2e2-4058-9e4e-f59a6970bd69") : "Office Professional Plus 2013",
uuid.UUID("b13afb38-cd79-4ae5-9f7f-eed058d750ca") : "Office Standard 2013",
uuid.UUID("3cfe50a9-0e03-4b29-9754-9f193f07b71f") : "Office Project Professional 2013 [Preview]",
uuid.UUID("4a5d124a-e620-44ba-b6ff-658961b33b9a") : "Office Project Professional 2013",
uuid.UUID("39e49e57-ae68-4ee3-b098-26480df3da96") : "Office Project Standard 2013 [Preview]",
uuid.UUID("427a28d1-d17c-4abf-b717-32c780ba6f07") : "Office Project Standard 2013",
uuid.UUID("cfbfd60e-0b5f-427d-917c-a4df42a80e44") : "Office Visio Professional 2013 [Preview]",
uuid.UUID("e13ac10e-75d0-4aff-a0cd-764982cf541c") : "Office Visio Professional 2013",
uuid.UUID("7012cc81-8887-42e9-b17d-4e5e42760f0d") : "Office Visio Standard 2013 [Preview]",
uuid.UUID("ac4efaf0-f81f-4f61-bdf7-ea32b02ab117") : "Office Visio Standard 2013",
uuid.UUID("44b538e2-fb34-4732-81e4-644c17d2e746") : "Office Access 2013 [Preview]",
uuid.UUID("6ee7622c-18d8-4005-9fb7-92db644a279b") : "Office Access 2013",
uuid.UUID("9373bfa0-97b3-4587-ab73-30934461d55c") : "Office Excel 2013 [Preview]",
uuid.UUID("f7461d52-7c2b-43b2-8744-ea958e0bd09a") : "Office Excel 2013",
uuid.UUID("67c0f908-184f-4f64-8250-12db797ab3c3") : "Office OneNote 2013 [Preview]",
uuid.UUID("efe1f3e6-aea2-4144-a208-32aa872b6545") : "Office OneNote 2013",
uuid.UUID("7bce4e7a-dd80-4682-98fa-f993725803d2") : "Office Outlook 2013 [Preview]",
uuid.UUID("771c3afa-50c5-443f-b151-ff2546d863a0") : "Office OutLook 2013",
uuid.UUID("1ec10c0a-54f6-453e-b85a-6fa1bbfea9b7") : "Office PowerPoint 2013 [Preview]",
uuid.UUID("8c762649-97d1-4953-ad27-b7e2c25b972e") : "Office PowerPoint 2013",
uuid.UUID("15aa2117-8f79-49a8-8317-753026d6a054") : "Office Publisher 2013 [Preview]",
uuid.UUID("00c79ff1-6850-443d-bf61-71cde0de305f") : "Office Publisher 2013",
uuid.UUID("7ccc8256-fbaa-49c6-b2a9-f5afb4257cd2") : "Office InfoPath 2013 [Preview]",
uuid.UUID("a30b8040-d68a-423f-b0b5-9ce292ea5a8f") : "Office InfoPath 2013",
uuid.UUID("c53dfe17-cc00-4967-b188-a088a965494d") : "Office Lync 2013 [Preview]",
uuid.UUID("1b9f11e3-c85c-4e1b-bb29-879ad2c909e3") : "Office Lync 2013",
uuid.UUID("de9c7eb6-5a85-420d-9703-fff11bdd4d43") : "Office Word 2013 [Preview]",
uuid.UUID("d9f5b1c6-5386-495a-88f9-9ad6b41ac9b3") : "Office Word 2013",
uuid.UUID("2816a87d-e1ed-4097-b311-e2341c57b179") : "Office Mondo 2013 [Preview]",
uuid.UUID("dc981c6b-fc8e-420f-aa43-f8f33e5c0923") : "Office Mondo 2013",
uuid.UUID("aa286eb4-556f-4eeb-967c-c1b771b7673e") : "Office SharePoint Workspace (Groove) 2013 [Preview]",
uuid.UUID("fb4875ec-0c6b-450f-b82b-ab57d8D1677f") : "Office SharePoint Workspace (Groove) 2013",
## uuid.UUID("???") : "Office SharePoint Designer (Frontpage) 2013 [Preview]",
uuid.UUID("ba3e3833-6a7e-445a-89d0-7802a9a68588") : "Office SharePoint Designer (Frontpage) 2013",
uuid.UUID("1dc00701-03af-4680-b2af-007ffc758a1f") : "Office Mondo Retail 2013",
#################
## Office 2010 ##
#################
uuid.UUID("6f327760-8c5c-417c-9b61-836a98287e0c") : "Office Professional Plus 2010",
uuid.UUID("9da2a678-fb6b-4e67-ab84-60dd6a9c819a") : "Office Standard 2010",
uuid.UUID("df133ff7-bf14-4f95-afe3-7b48e7e331ef") : "Office Project Professional 2010",
uuid.UUID("5dc7bf61-5ec9-4996-9ccb-df806a2d0efe") : "Office Project Standard 2010",
uuid.UUID("e558389c-83c3-4b29-adfe-5e4d7f46c358") : "Office Visio Professional 2010",
uuid.UUID("9ed833ff-4f92-4f36-b370-8683a4f13275") : "Office Visio Standard 2010",
uuid.UUID("92236105-bb67-494f-94c7-7f7a607929bd") : "Office Visio Premium 2010",
uuid.UUID("8ce7e872-188c-4b98-9d90-f8f90b7aad02") : "Office Access 2010",
uuid.UUID("cee5d470-6e3b-4fcc-8c2b-d17428568a9f") : "Office Excel 2010",
uuid.UUID("ab586f5c-5256-4632-962f-fefd8b49e6f4") : "Office OneNote 2010",
uuid.UUID("ecb7c192-73ab-4ded-acf4-2399b095d0cc") : "Office OutLook 2010",
uuid.UUID("45593b1d-dfb1-4e91-bbfb-2d5d0ce2227a") : "Office PowerPoint 2010",
uuid.UUID("b50c4f75-599b-43e8-8dcd-1081a7967241") : "Office Publisher 2010",
uuid.UUID("ca6b6639-4ad6-40ae-a575-14dee07f6430") : "Office InfoPath 2010",
uuid.UUID("8947d0b8-c33b-43e1-8c56-9b674c052832") : "Office SharePoint Workspace (Groove) 2010",
uuid.UUID("2d0882e7-a4e7-423b-8ccc-70d91e0158b1") : "Office Word 2010",
uuid.UUID("ea509e87-07a1-4a45-9edc-eba5a39f36af") : "Office Small Business Basics 2010",
uuid.UUID("2745e581-565a-4670-ae90-6bf7c57ffe43") : "Office Starter 2010 Retail",
## uuid.UUID("???") : "Office SharePoint Designer (Frontpage) 2010",
uuid.UUID("09ed9640-f020-400a-acd8-d7d867dfd9c2") : "Office Mondo 1 2010",
uuid.UUID("ef3d4e49-a53d-4d81-a2b1-2ca6c2556b2c") : "Office Mondo 2 2010",
######################
## Windows Previews ##
######################
uuid.UUID("a4383e6b-dada-423d-a43d-f25678429676") : "Windows 8.1 Professional (Blue) [Preview]",
uuid.UUID("631ead72-a8ab-4df8-bbdf-372029989bdd") : "Windows 8.1 ARM [Beta Pre-Release]",
uuid.UUID("2b9c337f-7a1d-4271-90a3-c6855a2b8a1c") : "Windows 8.1 [Beta Pre-Release]",
uuid.UUID("ba947c44-d19d-4786-b6ae-22770bc94c54") : "Windows Server 2016 Datacenter [Preview]",
uuid.UUID("ff808201-fec6-4fd4-ae16-abbddade5706") : "Windows 10 Professional [Pre-Release]",
uuid.UUID("cf59a07b-1a2a-4be0-bfe0-423b5823e663") : "Windows 8 Professional WMC [RC]",
#################################
## A lot of Previews to define ##
#################################
uuid.UUID("34260150-69ac-49a3-8a0d-4a403ab55763") : "Windows 10 Professional N [Pre-Release]",
uuid.UUID("64192251-81b0-4898-ac63-913cc3edf919") : "Windows XX [XX]",
uuid.UUID("cc17e18a-fa93-43d6-9179-72950a1e931a") : "Windows 10 Professional WMC [Pre-Release]",
uuid.UUID("903663f7-d2ab-49c9-8942-14aa9e0a9c72") : "Windows 10 Home / Core [Pre-Release]",
uuid.UUID("4dfd543d-caa6-4f69-a95f-5ddfe2b89567") : "Windows 10 Home / Core N [Pre-Release]",
uuid.UUID("6496e59d-89dc-49eb-a353-09ceb9404845") : "Windows 10 Home / Core [Pre-Release]",
uuid.UUID("2cc171ef-db48-4adc-af09-7c574b37f139") : "Windows 10 Home / Core Single Language [Pre-Release]",
uuid.UUID("5fe40dd6-cf1f-4cf2-8729-92121ac2e997") : "Windows 10 Home / Core Country Specific [Pre-Release]",
uuid.UUID("af43f7f0-3b1e-4266-a123-1fdb53f4323b") : "Windows 10 Education [Pre-Release]",
uuid.UUID("075aca1f-05d7-42e5-a3ce-e349e7be7078") : "Windows 10 Education N [Pre-Release]",
uuid.UUID("e8ced63e-420d-4ab6-8723-aaf165efb5eb") : "Windows XX Education [Pre-Release]",
uuid.UUID("3885bca5-11c1-4d4e-9395-df38f7f09a0e") : "Windows XX Education N [Pre-Release]",
uuid.UUID("6ae51eeb-c268-4a21-9aae-df74c38b586d") : "Windows 10 Enterprise N [Pre-Release]",
uuid.UUID("c23947f3-3f2e-401f-a38c-f38fe0ecb0bd") : "Windows XX Enterprise N [XX]",
uuid.UUID("38fbe2ac-465a-4ef7-b9d8-72044f2792b6") : "Windows XX Enterprise [XX]",
uuid.UUID("2cf5af84-abab-4ff0-83f8-f040fb2576eb") : "Windows 10 Enterprise XX LTSB [Pre-Release]",
uuid.UUID("11a37f09-fb7f-4002-bd84-f3ae71d11e90") : "Windows 10 Enterprise XX LTSB N [Pre-Release]",
uuid.UUID("75d003b0-dc66-42c0-b3a1-308a3f35741a") : "Windows 10 Enterprise XX LTSB [Pre-Release]",
uuid.UUID("4e4d5504-e7b1-419c-913d-3c80c15294fc") : "Windows 10 Enterprise XX LTSB N [Pre-Release]",
uuid.UUID("43f2ab05-7c87-4d56-b27c-44d0f9a3dabd") : "Windows 10 Enterprise [Pre-Release]",
uuid.UUID("b554b49f-4d57-4f08-955e-87886f514d49") : "Windows 10 Core ARM [Pre-Release]",
uuid.UUID("f18bbe32-16dc-48d4-a27b-5f3966f82513") : "Windows 10 Core Connected N [Pre-Release]",
uuid.UUID("964a60f6-1505-4ddb-af03-6a9ce6997d3b") : "Windows 10 Core Connected Single Language [Pre-Release]",
uuid.UUID("b5fe5eaa-14cc-4075-84ae-57c0206d1133") : "Windows 10 Core Connected Country Specific [Pre-Release]",
uuid.UUID("827a0032-dced-4609-ab6e-16b9d8a40280") : "Windows 10 Core Connected [Pre-Release]",
uuid.UUID("b15187db-11c6-4f13-91ca-8121cebf5b88") : "Windows 10 Professional S [Pre-Release]",
uuid.UUID("6cdbc9fb-63f5-431b-a5c0-c6f19ae26a9b") : "Windows 10 Professional S N [Pre-Release]",
uuid.UUID("aa234c15-ee34-4e5f-adb5-73afafb77143") : "Windows XX Professional S [Pre-Release]",
uuid.UUID("9f6a1bc9-5278-4991-88c9-7301c87a75ea") : "Windows XX Professional S N [Pre-Release]",
uuid.UUID("49066601-00dc-4d2c-83a8-4343a7b990d1") : "Windows 10 Professional Student [Pre-Release]",
uuid.UUID("bd64ebf7-d5ec-44c5-ba00-6813441c8c87") : "Windows 10 Professional Student N [Pre-Release]",
uuid.UUID("5b2add49-b8f4-42e0-a77c-adad4efeeeb1") : "Windows 10 PPIPro [Pre-Release]",
uuid.UUID("3a9a9414-24bf-4836-866d-ba13a298efb0") : "Windows 8 Core ARM [RC]",
uuid.UUID("c8cca3ca-bea8-4f6f-87e0-4d050ce8f0a9") : "Windows 8 Embedded Industry Enterprise [TAP-CTP]",
uuid.UUID("5ca3e488-dbae-4fae-8282-a98fbcd21126") : "Windows 8 Embedded Industry Enterprise [Beta]",
uuid.UUID("cde952c7-2f96-4d9d-8f2b-2d349f64fc51") : "Windows 8.1 Enterprise [Pre-Release]",
uuid.UUID("c436def1-0dcc-4849-9a59-8b6142eb70f3") : "Windows 8.1 Core Connected [Pre-Release]",
uuid.UUID("86f72c8d-8363-4188-b574-1a53cb374711") : "Windows 8.1 Core Connected N [Pre-Release]",
uuid.UUID("a8651bfb-7fe0-40df-b156-87337ecd5acc") : "Windows 8.1 Core Connected Country Specific [Pre-Release]",
uuid.UUID("5b120df4-ea3f-4e82-b0c0-6568f719730e") : "Windows 8.1 Core Connected Single Language [Pre-Release]",
uuid.UUID("fd5ae385-f5cf-4b53-b1fa-1af6fff7c0d8") : "Windows 8.1 Professional Student [Pre-Release]",
uuid.UUID("687f6358-6a21-453a-a712-3b3b57123827") : "Windows 8.1 Professional Student N [Pre-Release]",
uuid.UUID("c35a9336-fb02-48db-8f4d-245c17f03667") : "Windows 8.1 Embedded Industry [Beta]",
uuid.UUID("4daf1e3e-6be9-4848-8f5a-a18a0d2895e1") : "Windows 8.1 Embedded Industry Enterprise [Beta]",
uuid.UUID("9cc2564c-292e-4d8a-b9f9-1f5007d9409a") : "Windows 8.1 Embedded Industry Automotive [Beta]",
uuid.UUID("3ddb92aa-332e-46f9-abb7-8bdf62f8d967") : "Windows Longhorn Web Edition [XX]",
uuid.UUID("7ea4f647-9e67-453b-a7ba-56f7102afde2") : "Windows Longhorn Standard Server [XX]",
uuid.UUID("5a99526c-1c09-4481-80fb-b60e8b3d99f8") : "Windows Longhorn Enterprise Server [XX]",
uuid.UUID("8372b47d-5221-41d8-88d0-3f924e50623e") : "Windows Longhorn Computer Cluster [XX]",
uuid.UUID("932ef1f5-4327-4548-b147-51b0f5502995") : "Windows Longhorn Datacenter Server [XX]",
uuid.UUID("bebf03b1-a184-4c5e-9103-88af08055e68") : "Windows Longhorn Enterprise Server IA64 [XX]",
uuid.UUID("bfa6b683-56be-47b8-a22e-461b27b9cf11") : "Windows Server XX MultiPoint Standard [XX]",
uuid.UUID("bc20fb5b-4097-484f-84d2-55b18dac95eb") : "Windows Server XX MultiPoint Premium [XX]",
uuid.UUID("8a409d61-30fe-4903-bdbc-1fb28603ba3a") : "Windows Server XX Enterprise [XX]",
uuid.UUID("9dce1f29-bb10-4be0-8027-35b953dd46d5") : "Windows 7 Server Enterprise [XX]",
uuid.UUID("bf9eda2f-74cc-4ba3-8967-cde30f18c230") : "Windows 7 Server Enterprise IA64 [XX]",
uuid.UUID("dc06c019-b222-4706-a820-645e77d26a91") : "Windows 7 Server Enterprise without Hyper-V [XX]",
uuid.UUID("d3872724-5c08-4b1b-91f2-fc9eafed4990") : "Windows XX Server Standard [XX]",
uuid.UUID("92374131-ed4c-4d1b-846a-32f43c3eb90d") : "Windows 7 Server Standard [XX]",
uuid.UUID("f963bf4b-9693-46e6-9d9d-09c73eaa2b60") : "Windows 7 Server Standard without Hyper-V [XX]",
uuid.UUID("e5676f13-9b66-4a1f-8b0c-43490e236202") : "Windows XX Server Web [XX]",
uuid.UUID("4f4cfa6c-76d8-49f5-9c41-0a57f8af1bbc") : "Windows 7 Server Web [XX]",
uuid.UUID("0839e017-cfef-4ac6-a97e-ed2ea7962787") : "Windows 7 Server Datacenter without Hyper-V [XX]",
uuid.UUID("cc64c548-1867-4777-a1cc-0022691bc2a0") : "Windows 7 Server Datacenter [XX]",
uuid.UUID("2412bea9-b6e0-441e-8dc2-a13720b42de9") : "Windows XX Server HPC Edition [XX]",
uuid.UUID("c6e3410d-e48d-41eb-8ca9-848397f46d02") : "Windows Server 2012 N / Windows 8 Core N [RC]",
uuid.UUID("b148c3f4-6248-4d2f-8c6d-31cce7ae95c3") : "Windows Server 2012 Single Language / Windows 8 Core Single Language [RC]",
uuid.UUID("c7a8a09a-571c-4ea8-babc-0cbe4d48a89d") : "Windows Server 2012 Country Specific / Windows 8 Core Country Specific [RC]",
uuid.UUID("8f365ba6-c1b9-4223-98fc-282a0756a3ed") : "Windows Server 2012 R2 Essentials [RTM]",
uuid.UUID("b995b62c-eae2-40aa-afb9-111889a84ef4") : "Windows XX Server HI [Beta]",
uuid.UUID("99ff9b26-016a-49d3-982e-fc492f352e57") : "Windows Vista Business [XX]",
uuid.UUID("90284483-de09-44a2-a406-98957f8dd09d") : "Windows Vista Business [XX]",
uuid.UUID("af46f56f-f06b-49f0-a420-caa8a8d2bf8c") : "Windows Vista Business N [XX]",
uuid.UUID("cf67834d-db4a-402c-ab1f-2c134f02b700") : "Windows Vista Enterprise [XX]",
uuid.UUID("14478aca-ea15-4958-ac34-359281101c99") : "Windows Vista Enterprise [XX]",
uuid.UUID("0707c7fc-143d-46a4-a830-3705e908202c") : "Windows Vista Enterprise N [XX]",
uuid.UUID("957ec1e8-97cd-42a8-a091-01a30cf779da") : "Windows 7 Business [XX]",
uuid.UUID("0ff4e536-a746-4018-b107-e81dd0b6d33a") : "Windows 7 Business N [XX]",
uuid.UUID("ea77973e-4930-4fa1-a899-02dfaeada1db") : "Windows 7 Enterprise [XX]",
uuid.UUID("e4ecef68-4372-4740-98e8-6c157cd301c2") : "Windows 7 Enterprise N [XX]",
uuid.UUID("2a4403df-877f-4046-8271-db6fb6ec54c8") : "Enterprise ProdKey3 Win 9984 DLA/Bypass NQR Test",
uuid.UUID("38fbe2ac-465a-4ef7-b9d8-72044f2792b6") : "Windows XX Enterprise [XX]",
}
licenseStates = {
0 : "Unlicensed",
1 : "Activated",
2 : "Grace Period",
3 : "Out-of-Tolerance Grace Period",
4 : "Non-Genuine Grace Period",
5 : "Notifications Mode",
6 : "Extended Grace Period",
}
licenseStatesEnum = {
'unlicensed' : 0,
'licensed' : 1,
'oobGrace' : 2,
'ootGrace' : 3,
'nonGenuineGrace' : 4,
'notification' : 5,
'extendedGrace' : 6
}
errorCodes = {
'SL_E_VL_NOT_WINDOWS_SLP' : 0xC004F035,
'SL_E_VL_NOT_ENOUGH_COUNT' : 0xC004F038,
'SL_E_VL_BINDING_SERVICE_NOT_ENABLED' : 0xC004F039,
'SL_E_VL_INFO_PRODUCT_USER_RIGHT' : 0x4004F040,
'SL_I_VL_OOB_NO_BINDING_SERVER_REGISTRATION' : 0x4004F041,
'SL_E_VL_KEY_MANAGEMENT_SERVICE_ID_MISMATCH' : 0xC004F042,
'SL_E_VL_MACHINE_NOT_BOUND' : 0xC004F056
}
def __init__(self, data, config):
self.data = data
self.config = config
def getConfig(self):
return self.config
def getOptions(self):
return self.config
def getData(self):
return self.data
def getResponse(self):
return ''
def getResponsePadding(self, bodyLength):
if bodyLength % 8 == 0:
paddingLength = 0
else:
paddingLength = 8 - bodyLength % 8
padding = bytearray(paddingLength)
return padding
def serverLogic(self, kmsRequest):
if self.config['sqlite'] and self.config['dbSupport']:
self.dbName = 'clients.db'
if not os.path.isfile(self.dbName):
# Initialize the database.
con = None
try:
con = sqlite3.connect(self.dbName)
cur = con.cursor()
cur.execute("CREATE TABLE clients(clientMachineId TEXT, machineName TEXT, applicationId TEXT, skuId TEXT, licenseStatus TEXT, lastRequestTime INTEGER, kmsEpid TEXT, requestCount INTEGER)")
except sqlite3.Error, e:
logging.error("%s:" % e.args[0])
sys.exit(1)
finally:
if con:
con.commit()
con.close()
shell_message(nshell = 15)
logging.debug("KMS Request Bytes: \n%s\n" % justify(binascii.b2a_hex(str(kmsRequest))))
logging.debug("KMS Request: \n%s\n" % justify(kmsRequest.dump(print_to_stdout = False)))
clientMachineId = kmsRequest['clientMachineId'].get()
global applicationId
applicationId = kmsRequest['applicationId'].get()
skuId = kmsRequest['skuId'].get()
requestDatetime = filetimes.filetime_to_dt(kmsRequest['requestTime'])
# Try and localize the request time, if pytz is available
try:
import timezones
from pytz import utc
local_dt = utc.localize(requestDatetime).astimezone(timezones.localtz())
except ImportError:
local_dt = requestDatetime
infoDict = {
"machineName" : kmsRequest.getMachineName(),
"clientMachineId" : str(clientMachineId),
"appId" : self.appIds.get(applicationId, str(applicationId)),
"skuId" : self.skuIds.get(skuId, str(skuId)),
"licenseStatus" : kmsRequest.getLicenseStatus(),
"requestTime" : int(time.time()),
"kmsEpid" : None
}
#print infoDict
logging.info("Machine Name: %s" % infoDict["machineName"])
logging.info("Client Machine ID: %s" % infoDict["clientMachineId"])
logging.info("Application ID: %s" % infoDict["appId"])
logging.info("SKU ID: %s" % infoDict["skuId"])
logging.info("License Status: %s" % infoDict["licenseStatus"])
logging.info("Request Time: %s" % local_dt.strftime('%Y-%m-%d %H:%M:%S %Z (UTC%z)'))
if self.config['sqlite'] and self.config['dbSupport']:
con = None
try:
con = sqlite3.connect(self.dbName)
cur = con.cursor()
cur.execute("SELECT * FROM clients WHERE clientMachineId=:clientMachineId;", infoDict)
try:
data = cur.fetchone()
if not data:
#print "Inserting row..."
cur.execute("INSERT INTO clients (clientMachineId, machineName, applicationId, skuId, licenseStatus, lastRequestTime, requestCount) VALUES (:clientMachineId, :machineName, :appId, :skuId, :licenseStatus, :requestTime, 1);", infoDict)
else:
#print "Data:", data
if data[1] != infoDict["machineName"]:
cur.execute("UPDATE clients SET machineName=:machineName WHERE clientMachineId=:clientMachineId;", infoDict)
if data[2] != infoDict["appId"]:
cur.execute("UPDATE clients SET applicationId=:appId WHERE clientMachineId=:clientMachineId;", infoDict)
if data[3] != infoDict["skuId"]:
cur.execute("UPDATE clients SET skuId=:skuId WHERE clientMachineId=:clientMachineId;", infoDict)
if data[4] != infoDict["licenseStatus"]:
cur.execute("UPDATE clients SET licenseStatus=:licenseStatus WHERE clientMachineId=:clientMachineId;", infoDict)
if data[5] != infoDict["requestTime"]:
cur.execute("UPDATE clients SET lastRequestTime=:requestTime WHERE clientMachineId=:clientMachineId;", infoDict)
# Increment requestCount
cur.execute("UPDATE clients SET requestCount=requestCount+1 WHERE clientMachineId=:clientMachineId;", infoDict)
except sqlite3.Error, e:
logging.error("%s:" % e.args[0])
except sqlite3.Error, e:
logging.error("%s:" % e.args[0])
sys.exit(1)
finally:
if con:
con.commit()
con.close()
return self.createKmsResponse(kmsRequest)
def createKmsResponse(self, kmsRequest):
response = self.kmsResponseStruct()
response['versionMinor'] = kmsRequest['versionMinor']
response['versionMajor'] = kmsRequest['versionMajor']
#print " : ", kmsRequest['applicationId'] ----> This line was returning garbage in the pidGenerator
if not self.config["epid"]:
response["kmsEpid"] = kmsPidGenerator.epidGenerator(applicationId, kmsRequest['versionMajor'], self.config["lcid"]).encode('utf-16le')
else:
response["kmsEpid"] = self.config["epid"].encode('utf-16le')
response['clientMachineId'] = kmsRequest['clientMachineId']
response['responseTime'] = kmsRequest['requestTime']
response['currentClientCount'] = self.config["CurrentClientCount"]
response['vLActivationInterval'] = self.config["VLActivationInterval"]
response['vLRenewalInterval'] = self.config["VLRenewalInterval"]
if self.config['sqlite'] and self.config['dbSupport']:
con = None
try:
con = sqlite3.connect(self.dbName)
cur = con.cursor()
cur.execute("SELECT * FROM clients WHERE clientMachineId=?;", [str(kmsRequest['clientMachineId'].get())])
try:
data = cur.fetchone()
#print "Data:", data
if data[6]:
response["kmsEpid"] = data[6].encode('utf-16le')
else:
cur.execute("UPDATE clients SET kmsEpid=? WHERE clientMachineId=?;", (str(response["kmsEpid"].decode('utf-16le')), str(kmsRequest['clientMachineId'].get())))
except sqlite3.Error, e:
logging.error("%s:" % e.args[0])
except sqlite3.Error, e:
logging.error("%s:" % e.args[0])
sys.exit(1)
finally:
if con:
con.commit()
con.close()
logging.info("Server ePID: %s" % response["kmsEpid"].decode('utf-16le').encode('utf-8'))
return response
import kmsRequestV4, kmsRequestV5, kmsRequestV6, kmsRequestUnknown
def generateKmsResponseData(data, config):
version = kmsBase.GenericRequestHeader(data)['versionMajor']
currentDate = datetime.datetime.now().ctime()
if version == 4:
logging.info("Received V%d request on %s." % (version, currentDate))
messagehandler = kmsRequestV4.kmsRequestV4(data, config)
messagehandler.executeRequestLogic()
elif version == 5:
logging.info("Received V%d request on %s." % (version, currentDate))
messagehandler = kmsRequestV5.kmsRequestV5(data, config)
messagehandler.executeRequestLogic()
elif version == 6:
logging.info("Received V%d request on %s." % (version, currentDate))
messagehandler = kmsRequestV6.kmsRequestV6(data, config)
messagehandler.executeRequestLogic()
else:
logging.info("Unhandled KMS version V%d." % version)
messagehandler = kmsRequestUnknown.kmsRequestUnknown(data, config)
return messagehandler.getResponse()

136
py2-kms/kmsPidGenerator.py Normal file
View file

@ -0,0 +1,136 @@
#!/usr/bin/env python
import datetime
import random
import time
import uuid
APP_ID_WINDOWS = uuid.UUID("55C92734-D682-4D71-983E-D6EC3F16059F")
APP_ID_OFFICE14 = uuid.UUID("59A52881-A989-479D-AF46-F275C6370663")
APP_ID_OFFICE15 = uuid.UUID("0FF1CE15-A989-479D-AF46-F275C6370663")
# KMS Host OS Type
hostOsList = {}
# Windows Server 2008 R2 SP1
hostOsList["HOST_SERVER2008R2"] = {
"type" : 55041,
"osBuild" : 7601
}
# Windows Server 2012 RTM
hostOsList["HOST_SERVER2012"] = {
"type" : 5426,
"osBuild" : 9200
}
# Windows Server 2012 R2 RTM
hostOsList["HOST_SERVER2012R2"] = {
"type" : 6401,
"osBuild" : 9600
}
# Windows Server 2016 RTM
hostOsList["HOST_SERVER2016"] = {
"type" : 3612,
"osBuild" : 14393
}
# Product Specific KeyConfig
pkeyConfigList = {}
# Windows Server KMS Host PID, actual PIDRangeMax = 191999999
pkeyConfigList["windows"] = {
"GroupID" : 206,
"PIDRangeMin" : 152000000,
"PIDRangeMax" : 191999999
}
# Windows Server 2012 R2 KMS Host PID, actual PIDRangeMax = 310999999
pkeyConfigList["windows2012r2"] = {
"GroupID" : 206,
"PIDRangeMin" : 271000000,
"PIDRangeMax" : 310999999
}
# Office 2010 KMSHost Class PID, actual PIDRangeMax = 217999999
pkeyConfigList["office14"] = {
"GroupID" : 96,
"PIDRangeMin" : 199000000,
"PIDRangeMax" : 217999999
}
# Office 2013 KMSHost Class PID, actual PIDRangeMax = 255999999
pkeyConfigList["office15"] = {
"GroupID" : 206,
"PIDRangeMin" : 234000000,
"PIDRangeMax" : 255999999
}
def epidGenerator(appId, version, lcid):
# Generate Part 1 & 7: Host Type and KMS Server OS Build
hostOsType = random.choice(hostOsList.keys())
hostOsDict = hostOsList[hostOsType]
# Generate Part 2: Group ID and Product Key ID Range
if appId == APP_ID_OFFICE14:
keyConfig = pkeyConfigList["office14"]
elif appId == APP_ID_OFFICE15:
keyConfig = pkeyConfigList["office15"]
else:
# Default to Windows
if hostOsDict['osBuild'] == 14393:
keyConfig = pkeyConfigList["windows2012r2"]
elif hostOsDict['osBuild'] == 9600:
keyConfig = pkeyConfigList["windows2012r2"]
else:
keyConfig = pkeyConfigList["windows"]
# Generate Part 3 and Part 4: Product Key ID
productKeyID = random.randint(keyConfig["PIDRangeMin"], keyConfig["PIDRangeMax"])
# Generate Part 5: License Channel (00=Retail, 01=Retail, 02=OEM,
# 03=Volume(GVLK,MAK)) - always 03
licenseChannel = 3
# Generate Part 6: Language - use system default language
# 1033 is en-us
languageCode = lcid # C# CultureInfo.InstalledUICulture.LCID
# Generate Part 8: KMS Host Activation Date
# Get Minimum Possible Date: Newer Products first
if hostOsType == "HOST_SERVER2016":
# Microsoft Windows Server 2016 RTM
minTime = datetime.date(2016, 7, 27)
elif hostOsType == "HOST_SERVER2012R2" or version == 6:
# Microsoft Windows Server 2012 R2 RTM (October 17, 2013)
minTime = datetime.date(2013, 10, 17)
elif appId == APP_ID_OFFICE15:
# Microsoft Office 2013 RTM (October 24, 2012)
minTime = datetime.date(2012, 10, 24)
elif hostOsType == "HOST_SERVER2012" or version == 5:
# Microsoft Windows Server 2012 RTM (September 4, 2012)
minTime = datetime.date(2012, 9, 4)
else:
# Windows Server 2008 R2 SP1 (February 16, 2011)
minTime = datetime.date(2011, 2, 16)
# Generate Year and Day Number
randomDate = datetime.date.fromtimestamp(random.randint(time.mktime(minTime.timetuple()), time.mktime(datetime.datetime.now().timetuple())))
firstOfYear = datetime.date(randomDate.year, 1, 1)
randomDayNumber = int((time.mktime(randomDate.timetuple()) - time.mktime(firstOfYear.timetuple())) / 86400 + 0.5)
# generate the epid string
result = []
result.append(str(hostOsDict["type"]).rjust(5, "0"))
result.append("-")
result.append(str(keyConfig["GroupID"]).rjust(5, "0"))
result.append("-")
result.append(str(productKeyID / 1000000).rjust(3, "0"))
result.append("-")
result.append(str(productKeyID % 1000000).rjust(6, "0"))
result.append("-")
result.append(str(licenseChannel).rjust(2, "0"))
result.append("-")
result.append(str(languageCode))
result.append("-")
result.append(str(hostOsDict["osBuild"]).rjust(4, "0"))
result.append(".0000-")
result.append(str(randomDayNumber).rjust(3, "0"))
result.append(str(randomDate.year).rjust(4, "0"))
return "".join(result)

View file

@ -0,0 +1,13 @@
#!/usr/bin/env python
import struct
from kmsBase import kmsBase
class kmsRequestUnknown(kmsBase):
def getResponse(self):
finalResponse = bytearray()
finalResponse.extend(bytearray(struct.pack('<I', 0)))
finalResponse.extend(bytearray(struct.pack('<I', 0)))
finalResponse.extend(bytearray(struct.pack('<I', self.errorCodes['SL_E_VL_KEY_MANAGEMENT_SERVICE_ID_MISMATCH'])))
return str(finalResponse)

125
py2-kms/kmsRequestV4.py Normal file
View file

@ -0,0 +1,125 @@
#!/usr/bin/env python
import binascii
import struct
import time
import logging
from kmsBase import kmsBase
from structure import Structure
from aes import AES
from formatText import shell_message, justify
# v4 AES Key
key = bytearray([0x05, 0x3D, 0x83, 0x07, 0xF9, 0xE5, 0xF0, 0x88, 0xEB, 0x5E, 0xA6, 0x68, 0x6C, 0xF0, 0x37, 0xC7, 0xE4, 0xEF, 0xD2, 0xD6])
# Xor Buffer
def xorBuffer(source, offset, destination, size):
for i in range(0, size):
destination[i] ^= source[i + offset]
class kmsRequestV4(kmsBase):
class RequestV4(Structure):
commonHdr = ()
structure = (
('bodyLength1', '<I'),
('bodyLength2', '<I'),
('request', ':', kmsBase.kmsRequestStruct),
('hash', '16s'),
('padding', ':'),
)
class ResponseV4(Structure):
commonHdr = ()
structure = (
('bodyLength1', '<I=len(response) + len(hash)'),
('unknown', '!I=0x00000200'),
('bodyLength2', '<I=len(response) + len(hash)'),
('response', ':', kmsBase.kmsResponseStruct),
('hash', '16s'),
('padding', ':'),
)
def executeRequestLogic(self):
requestData = self.RequestV4(self.data)
response = self.serverLogic(requestData['request'])
hash = self.generateHash(bytearray(str(response)))
self.responseData = self.generateResponse(response, hash)
time.sleep(1) # request sent back too quick for Windows 2008 R2, slow it down.
def generateHash(self, message):
"""
The KMS v4 hash is a variant of CMAC-AES-128. There are two key differences:
* The 'AES' used is modified in particular ways:
* The basic algorithm is Rjindael with a conceptual 160bit key and 128bit blocks.
This isn't part of the AES standard, but it works the way you'd expect.
Accordingly, the algorithm uses 11 rounds and a 192 byte expanded key.
* The trailing block is not XORed with a generated subkey, as defined in CMAC.
This is probably because the subkey generation algorithm is only defined for
situations where block and key size are the same.
"""
aes = AES()
messageSize = len(message)
lastBlock = bytearray(16)
hashBuffer = bytearray(16)
# MessageSize / Blocksize
j = messageSize >> 4
# Remainding bytes
k = messageSize & 0xf
# Hash
for i in range(0, j):
xorBuffer(message, i << 4, hashBuffer, 16)
hashBuffer = bytearray(aes.encrypt(hashBuffer, key, len(key)))
# Bit Padding
ii = 0
for i in range(j << 4, k + (j << 4)):
lastBlock[ii] = message[i]
ii += 1
lastBlock[k] = 0x80
xorBuffer(lastBlock, 0, hashBuffer, 16)
hashBuffer = bytearray(aes.encrypt(hashBuffer, key, len(key)))
return str(hashBuffer)
def generateResponse(self, responseBuffer, hash):
bodyLength = len(responseBuffer) + len(hash)
response = self.ResponseV4()
response['response'] = responseBuffer
response['hash'] = hash
response['padding'] = self.getResponsePadding(bodyLength)
shell_message(nshell = 16)
logging.debug("KMS V4 Response: %s" % justify(response.dump(print_to_stdout = False)))
logging.debug("KMS V4 Response Bytes: %s" % justify(binascii.b2a_hex(str(response))))
return str(response)
def getResponse(self):
return self.responseData
def generateRequest(self, requestBase):
hash = str(self.generateHash(bytearray(str(requestBase))))
bodyLength = len(requestBase) + len(hash)
request = kmsRequestV4.RequestV4()
request['bodyLength1'] = bodyLength
request['bodyLength2'] = bodyLength
request['request'] = requestBase
request['hash'] = hash
request['padding'] = self.getResponsePadding(bodyLength)
shell_message(nshell = 10)
logging.debug("Request V4 Data: %s" % justify(request.dump(print_to_stdout = False)))
logging.debug("Request V4: %s" % justify(binascii.b2a_hex(str(request))))
return request

175
py2-kms/kmsRequestV5.py Normal file
View file

@ -0,0 +1,175 @@
#!/usr/bin/env python
import logging
import binascii
import hashlib
import random
import struct
import aes
from kmsBase import kmsBase
from structure import Structure
from formatText import justify, shell_message
class kmsRequestV5(kmsBase):
class RequestV5(Structure):
class Message(Structure):
commonHdr = ()
structure = (
('salt', '16s'),
('encrypted', '236s'), #kmsBase.kmsRequestStruct
('padding', ':'),
)
commonHdr = ()
structure = (
('bodyLength1', '<I'),
('bodyLength2', '<I'),
('versionMinor', '<H'),
('versionMajor', '<H'),
('message', ':', Message),
)
class DecryptedRequest(Structure):
commonHdr = ()
structure = (
('salt', '16s'),
('request', ':', kmsBase.kmsRequestStruct),
)
class ResponseV5(Structure):
commonHdr = ()
structure = (
('bodyLength1', '<I=2 + 2 + len(salt) + len(encrypted)'),
('unknown', '!I=0x00000200'),
('bodyLength2', '<I=2 + 2 + len(salt) + len(encrypted)'),
('versionMinor', '<H'),
('versionMajor', '<H'),
('salt', '16s'),
('encrypted', ':'), #DecryptedResponse
('padding', ':'),
)
class DecryptedResponse(Structure):
commonHdr = ()
structure = (
('response', ':', kmsBase.kmsResponseStruct),
('keys', '16s'),
('hash', '32s'),
)
key = bytearray([ 0xCD, 0x7E, 0x79, 0x6F, 0x2A, 0xB2, 0x5D, 0xCB, 0x55, 0xFF, 0xC8, 0xEF, 0x83, 0x64, 0xC4, 0x70 ])
v6 = False
ver = 5
def executeRequestLogic(self):
self.requestData = self.RequestV5(self.data)
decrypted = self.decryptRequest(self.requestData)
responseBuffer = self.serverLogic(decrypted['request'])
iv, encrypted = self.encryptResponse(self.requestData, decrypted, responseBuffer)
self.responseData = self.generateResponse(iv, encrypted)
def decryptRequest(self, request):
encrypted = bytearray(str(request['message']))
iv = bytearray(request['message']['salt'])
moo = aes.AESModeOfOperation()
moo.aes.v6 = self.v6
decrypted = moo.decrypt(encrypted, 256, moo.modeOfOperation["CBC"], self.key, moo.aes.keySize["SIZE_128"], iv)
decrypted = aes.strip_PKCS7_padding(decrypted)
return self.DecryptedRequest(decrypted)
def encryptResponse(self, request, decrypted, response):
randomSalt = self.getRandomSalt()
sha256 = hashlib.sha256()
sha256.update(str(randomSalt))
result = sha256.digest()
iv = bytearray(request['message']['salt'])
randomStuff = bytearray(16)
for i in range(0,16):
randomStuff[i] = (bytearray(decrypted['salt'])[i] ^ iv[i] ^ randomSalt[i]) & 0xff
responsedata = self.DecryptedResponse()
responsedata['response'] = response
responsedata['keys'] = str(randomStuff)
responsedata['hash'] = result
padded = aes.append_PKCS7_padding(str(responsedata))
moo = aes.AESModeOfOperation()
moo.aes.v6 = self.v6
mode, orig_len, crypted = moo.encrypt(padded, moo.modeOfOperation["CBC"], self.key, moo.aes.keySize["SIZE_128"], iv)
return str(iv), str(bytearray(crypted))
def decryptResponse(self, response):
paddingLength = response['bodyLength1'] % 8
iv = bytearray(response['salt'])
encrypted = bytearray(response['encrypted'][:-paddingLength])
moo = aes.AESModeOfOperation()
moo.aes.v6 = self.v6
decrypted = moo.decrypt(encrypted, 256, moo.modeOfOperation["CBC"], self.key, moo.aes.keySize["SIZE_128"], iv)
decrypted = aes.strip_PKCS7_padding(decrypted)
return self.DecryptedResponse(decrypted)
def getRandomSalt(self):
return bytearray(random.getrandbits(8) for i in range(16))
def generateResponse(self, iv, encryptedResponse):
bodyLength = 4 + len(iv) + len(encryptedResponse)
response = self.ResponseV5()
response['versionMinor'] = self.requestData['versionMinor']
response['versionMajor'] = self.requestData['versionMajor']
response['salt'] = iv
response['encrypted'] = encryptedResponse
response['padding'] = self.getResponsePadding(bodyLength)
shell_message(nshell = 16)
logging.info("KMS V%d Response: \n%s\n" % (self.ver, justify(response.dump(print_to_stdout = False))))
logging.info("KMS V%d Structure Bytes: \n%s\n" % (self.ver, justify(binascii.b2a_hex(str(response)))))
return str(response)
def getResponse(self):
return self.responseData
def generateRequest(self, requestBase):
esalt = self.getRandomSalt()
moo = aes.AESModeOfOperation()
moo.aes.v6 = self.v6
dsalt = moo.decrypt(esalt, 16, moo.modeOfOperation["CBC"], self.key, moo.aes.keySize["SIZE_128"], esalt)
dsalt = bytearray(dsalt)
decrypted = self.DecryptedRequest()
decrypted['salt'] = str(dsalt)
decrypted['request'] = requestBase
padded = aes.append_PKCS7_padding(str(decrypted))
mode, orig_len, crypted = moo.encrypt(padded, moo.modeOfOperation["CBC"], self.key, moo.aes.keySize["SIZE_128"], esalt)
message = self.RequestV5.Message(str(bytearray(crypted)))
bodyLength = len(message) + 2 + 2
request = self.RequestV5()
request['bodyLength1'] = bodyLength
request['bodyLength2'] = bodyLength
request['versionMinor'] = requestBase['versionMinor']
request['versionMajor'] = requestBase['versionMajor']
request['message'] = message
shell_message(nshell = 10)
logging.info("Request V%d Data: \n%s\n" % (self.ver, justify(request.dump(print_to_stdout = False))))
logging.info("Request V%d: \n%s\n" % (self.ver, justify(binascii.b2a_hex(str(request)))))
return request

108
py2-kms/kmsRequestV6.py Normal file
View file

@ -0,0 +1,108 @@
#!/usr/bin/env python
import binascii
import hashlib
import hmac
import random
import struct
import aes
from kmsBase import kmsBase
from kmsRequestV5 import kmsRequestV5
from structure import Structure
class kmsRequestV6(kmsRequestV5):
class DecryptedResponse(Structure):
class Message(Structure):
commonHdr = ()
structure = (
('response', ':', kmsBase.kmsResponseStruct),
('keys', '16s'),
('hash', '32s'),
('hwid', '8s'),
('xorSalts', '16s'),
)
commonHdr = ()
structure = (
('message', ':', Message),
('hmac', '16s'),
)
key = bytearray([ 0xA9, 0x4A, 0x41, 0x95, 0xE2, 0x01, 0x43, 0x2D, 0x9B, 0xCB, 0x46, 0x04, 0x05, 0xD8, 0x4A, 0x21 ])
v6 = True
ver = 6
def encryptResponse(self, request, decrypted, response):
randomSalt = self.getRandomSalt()
sha256 = hashlib.sha256()
sha256.update(str(randomSalt))
result = sha256.digest()
SaltC = bytearray(request['message']['salt'])
DSaltC = bytearray(decrypted['salt'])
randomStuff = bytearray(16)
for i in range(0,16):
randomStuff[i] = (SaltC[i] ^ DSaltC[i] ^ randomSalt[i]) & 0xff
# XorSalts
XorSalts = bytearray(16)
for i in range (0, 16):
XorSalts[i] = (SaltC[i] ^ DSaltC[i]) & 0xff
message = self.DecryptedResponse.Message()
message['response'] = response
message['keys'] = str(randomStuff)
message['hash'] = result
message['xorSalts'] = str(XorSalts)
message['hwid'] = self.config['hwid']
# SaltS
SaltS = self.getRandomSalt()
moo = aes.AESModeOfOperation()
moo.aes.v6 = True
d = moo.decrypt(SaltS, 16, moo.modeOfOperation["CBC"], self.key, moo.aes.keySize["SIZE_128"], SaltS)
# DSaltS
DSaltS = bytearray(d)
# HMacMsg
HMacMsg = bytearray(16)
for i in range (0, 16):
HMacMsg[i] = (SaltS[i] ^ DSaltS[i]) & 0xff
HMacMsg.extend(str(message))
# HMacKey
requestTime = decrypted['request']['requestTime']
HMacKey = self.getMACKey(requestTime)
HMac = hmac.new(HMacKey, str(HMacMsg), hashlib.sha256)
digest = HMac.digest()
responsedata = self.DecryptedResponse()
responsedata['message'] = message
responsedata['hmac'] = digest[16:]
padded = aes.append_PKCS7_padding(str(responsedata))
mode, orig_len, crypted = moo.encrypt(str(padded), moo.modeOfOperation["CBC"], self.key, moo.aes.keySize["SIZE_128"], SaltS)
return str(SaltS), str(bytearray(crypted))
def getMACKey(self, t):
c1 = 0x00000022816889BD
c2 = 0x000000208CBAB5ED
c3 = 0x3156CD5AC628477A
i1 = (t / c1) & 0xFFFFFFFFFFFFFFFF
i2 = (i1 * c2) & 0xFFFFFFFFFFFFFFFF
seed = (i2 + c3) & 0xFFFFFFFFFFFFFFFF
sha256 = hashlib.sha256()
sha256.update(struct.pack("<Q", seed))
digest = sha256.digest()
return digest[16:]

6
py2-kms/randomHWID.py Normal file
View file

@ -0,0 +1,6 @@
#!/usr/bin/env python
import uuid
key = uuid.uuid4().hex
print key[:16]

37
py2-kms/randomPID.py Normal file
View file

@ -0,0 +1,37 @@
#!/usr/bin/env python
import uuid
import kmsPidGenerator
# Variables.
# 1033 (english) is en-us
# 1034 (spanish) is es-es
# 1041 (japanese) is ja
lcid = 1033
applicationId = uuid.UUID("55C92734-D682-4D71-983E-D6EC3F16059F") # Windows
applicationId2 = uuid.UUID("0FF1CE15-A989-479D-AF46-F275C6370663") # Office 15 (2013) and Office 16 (2016)
applicationId3 = uuid.UUID("59A52881-A989-479D-AF46-F275C6370663") # Office 14 (2010)
# KMS Version.
# 6 for date starting October 17, 2013
# 5 for date starting September 4, 2012
# 4 for date starting February 16, 2011
versionMajor = 6
# Responses.
response = kmsPidGenerator.epidGenerator(applicationId, versionMajor, lcid)
response2 = kmsPidGenerator.epidGenerator(applicationId2, versionMajor, lcid)
response3 = kmsPidGenerator.epidGenerator(applicationId3, versionMajor, lcid)
print "\nFor Windows: ", response
print "\nFor Office 2013/2016: ", response2
print "\nFor Office 2010: ", response3
#-----------------------------------------------------------------------------
# HWID Section.
import uuid
key = uuid.uuid4().hex
print "\nRandom hwid: ", key[:16]
print "\n"

62
py2-kms/rpcBase.py Normal file
View file

@ -0,0 +1,62 @@
#!/usr/bin/env python
import struct
import uuid
class rpcBase:
packetType = {
'request': 0,
'ping': 1,
'response': 2,
'fault': 3,
'working': 4,
'nocall': 5,
'reject': 6,
'ack': 7,
'clCancel': 8,
'fack': 9,
'cancelAck': 10,
'bindReq': 11,
'bindAck': 12,
'bindNak': 13,
'alterContext': 14,
'alterContextResp': 15,
'shutdown': 17,
'coCancel': 18,
'orphaned': 19
}
packetFlags = {
'firstFrag': 1, # 0x01
'lastFrag': 2, # 0x02
'cancelPending': 4, # 0x04
'reserved': 8, # 0x08
'multiplex': 16, # 0x10
'didNotExecute': 32, # 0x20
'maybe': 64, # 0x40
'objectUuid': 128 # 0x80
}
def __init__(self, data, config):
self.data = data
self.config = config
def populate(self):
self.requestData = self.parseRequest()
self.responseData = self.generateResponse()
return self
def getConfig(self):
return self.config
def getOptions(self):
return self.config
def getData(self):
return self.data
def parseRequest(self):
return {}
def getResponse(self):
return self.responseData

169
py2-kms/rpcBind.py Normal file
View file

@ -0,0 +1,169 @@
#!/usr/bin/env python
import logging
import binascii
import struct
import uuid
import rpcBase
from dcerpc import MSRPCHeader, MSRPCBindAck
from structure import Structure
from formatText import shell_message, justify
uuidNDR32 = uuid.UUID('8a885d04-1ceb-11c9-9fe8-08002b104860')
uuidNDR64 = uuid.UUID('71710533-beba-4937-8319-b5dbef9ccc36')
uuidTime = uuid.UUID('6cb71c2c-9812-4540-0300-000000000000')
uuidEmpty = uuid.UUID('00000000-0000-0000-0000-000000000000')
class CtxItem(Structure):
structure = (
('ContextID', '<H=0'),
('TransItems', 'B=0'),
('Pad', 'B=0'),
('AbstractSyntaxUUID', '16s=""'),
('AbstractSyntaxVer', '<I=0'),
('TransferSyntaxUUID', '16s=""'),
('TransferSyntaxVer', '<I=0'),
)
def ts(self):
return uuid.UUID(bytes_le=self['TransferSyntaxUUID'])
class CtxItemResult(Structure):
structure = (
('Result', '<H=0'),
('Reason', '<H=0'),
('TransferSyntaxUUID', '16s=""'),
('TransferSyntaxVer', '<I=0'),
)
def __init__(self, result, reason, tsUUID, tsVer):
Structure.__init__(self)
self['Result'] = result
self['Reason'] = reason
self['TransferSyntaxUUID'] = tsUUID.bytes_le
self['TransferSyntaxVer'] = tsVer
class MSRPCBind(Structure):
class CtxItemArray:
def __init__(self, data):
self.data = data
def __len__(self):
return len(self.data)
def __str__(self):
return self.data
def __getitem__(self, i):
return CtxItem(self.data[(len(CtxItem()) * i):])
_CTX_ITEM_LEN = len(CtxItem())
structure = (
('max_tfrag', '<H=4280'),
('max_rfrag', '<H=4280'),
('assoc_group', '<L=0'),
('ctx_num', 'B=0'),
('Reserved', 'B=0'),
('Reserved2', '<H=0'),
('_ctx_items', '_-ctx_items', 'self["ctx_num"]*self._CTX_ITEM_LEN'),
('ctx_items', ':', CtxItemArray),
)
class handler(rpcBase.rpcBase):
def parseRequest(self):
request = MSRPCHeader(self.data)
shell_message(nshell = 3)
logging.debug("RPC Bind Request Bytes: \n%s\n" % justify(binascii.b2a_hex(self.data)))
logging.debug("RPC Bind Request: \n%s\n%s\n" % (justify(request.dump(print_to_stdout = False)),
justify(MSRPCBind(request['pduData']).dump(print_to_stdout = False))))
return request
def generateResponse(self):
response = MSRPCBindAck()
request = self.requestData
bind = MSRPCBind(request['pduData'])
response['ver_major'] = request['ver_major']
response['ver_minor'] = request['ver_minor']
response['type'] = self.packetType['bindAck']
response['flags'] = self.packetFlags['firstFrag'] | self.packetFlags['lastFrag'] | self.packetFlags['multiplex']
response['representation'] = request['representation']
response['frag_len'] = 36 + bind['ctx_num'] * 24
response['auth_len'] = request['auth_len']
response['call_id'] = request['call_id']
response['max_tfrag'] = bind['max_tfrag']
response['max_rfrag'] = bind['max_rfrag']
response['assoc_group'] = 0x1063bf3f
port = str(self.config['port'])
response['SecondaryAddrLen'] = len(port) + 1
response['SecondaryAddr'] = port
pad = (4-((response["SecondaryAddrLen"]+MSRPCBindAck._SIZE) % 4))%4
response['Pad'] = '\0' * pad
response['ctx_num'] = bind['ctx_num']
preparedResponses = {}
preparedResponses[uuidNDR32] = CtxItemResult(0, 0, uuidNDR32, 2)
preparedResponses[uuidNDR64] = CtxItemResult(2, 2, uuidEmpty, 0)
preparedResponses[uuidTime] = CtxItemResult(3, 3, uuidEmpty, 0)
response['ctx_items'] = ''
for i in range (0, bind['ctx_num']):
ts_uuid = bind['ctx_items'][i].ts()
resp = preparedResponses[ts_uuid]
response['ctx_items'] += str(resp)
shell_message(nshell = 4)
logging.debug("RPC Bind Response: \n%s\n" % justify(response.dump(print_to_stdout = False)))
logging.debug("RPC Bind Response Bytes: \n%s\n" % justify(binascii.b2a_hex(str(response))))
return response
def generateRequest(self):
firstCtxItem = CtxItem()
firstCtxItem['ContextID'] = 0
firstCtxItem['TransItems'] = 1
firstCtxItem['Pad'] = 0
firstCtxItem['AbstractSyntaxUUID'] = uuid.UUID('51c82175-844e-4750-b0d8-ec255555bc06').bytes_le
firstCtxItem['AbstractSyntaxVer'] = 1
firstCtxItem['TransferSyntaxUUID'] = uuidNDR32.bytes_le
firstCtxItem['TransferSyntaxVer'] = 2
secondCtxItem = CtxItem()
secondCtxItem['ContextID'] = 1
secondCtxItem['TransItems'] = 1
secondCtxItem['Pad'] = 0
secondCtxItem['AbstractSyntaxUUID'] = uuid.UUID('51c82175-844e-4750-b0d8-ec255555bc06').bytes_le
secondCtxItem['AbstractSyntaxVer'] = 1
secondCtxItem['TransferSyntaxUUID'] = uuidTime.bytes_le
secondCtxItem['TransferSyntaxVer'] = 1
bind = MSRPCBind()
bind['max_tfrag'] = 5840
bind['max_rfrag'] = 5840
bind['assoc_group'] = 0
bind['ctx_num'] = 2
bind['ctx_items'] = bind.CtxItemArray(str(firstCtxItem)+str(secondCtxItem))
request = MSRPCHeader()
request['ver_major'] = 5
request['ver_minor'] = 0
request['type'] = self.packetType['bindReq']
request['flags'] = self.packetFlags['firstFrag'] | self.packetFlags['lastFrag'] | self.packetFlags['multiplex']
request['call_id'] = self.config['call_id']
request['pduData'] = str(bind)
shell_message(nshell = 0)
logging.debug("RPC Bind Request: \n%s\n%s\n" % (justify(request.dump(print_to_stdout = False)),
justify(MSRPCBind(request['pduData']).dump(print_to_stdout = False))))
logging.debug("RPC Bind Request Bytes: \n%s\n" % justify(binascii.b2a_hex(str(request))))
return request
def parseResponse(self):
return response

67
py2-kms/rpcRequest.py Normal file
View file

@ -0,0 +1,67 @@
#!/usr/bin/env python
import binascii
import logging
import struct
import uuid
from dcerpc import MSRPCRequestHeader, MSRPCRespHeader
import kmsBase
import rpcBase
from formatText import justify, shell_message
class handler(rpcBase.rpcBase):
def parseRequest(self):
request = MSRPCRequestHeader(self.data)
shell_message(nshell = 14)
logging.debug("RPC Message Request Bytes: \n%s\n" % justify(binascii.b2a_hex(self.data)))
logging.debug("RPC Message Request: \n%s\n" % justify(request.dump(print_to_stdout = False)))
return request
def generateResponse(self):
request = self.requestData
responseData = kmsBase.generateKmsResponseData(request['pduData'], self.config)
envelopeLength = len(responseData)
response = MSRPCRespHeader()
response['ver_major'] = request['ver_major']
response['ver_minor'] = request['ver_minor']
response['type'] = self.packetType['response']
response['flags'] = self.packetFlags['firstFrag'] | self.packetFlags['lastFrag']
response['representation'] = request['representation']
response['call_id'] = request['call_id']
response['alloc_hint'] = envelopeLength
response['ctx_id'] = request['ctx_id']
response['cancel_count'] = 0
response['pduData'] = responseData
shell_message(nshell = 17)
logging.debug("RPC Message Response: \n%s\n" % justify(response.dump(print_to_stdout = False)))
logging.debug("RPC Message Response Bytes: \n%s\n" % justify(binascii.b2a_hex(str(response))))
return response
def generateRequest(self):
request = MSRPCRequestHeader()
request['ver_major'] = 5
request['ver_minor'] = 0
request['type'] = self.packetType['request']
request['flags'] = self.packetFlags['firstFrag'] | self.packetFlags['lastFrag']
request['representation'] = 0x10
request['call_id'] = self.config['call_id']
request['alloc_hint'] = len(self.data)
request['pduData'] = str(self.data)
shell_message(nshell = 11)
logging.debug("RPC Message Request: \n%s\n" % justify(request.dump(print_to_stdout = False)))
logging.debug("RPC Message Request Bytes: \n%s\n" % justify(binascii.b2a_hex(str(request))))
return request
def parseResponse(self):
return response

134
py2-kms/server.py Normal file
View file

@ -0,0 +1,134 @@
#!/usr/bin/env python
import argparse
import binascii
import hashlib
import random
import re
import socket
import SocketServer
import struct
import uuid
import logging
import os
import rpcBind, rpcRequest
from dcerpc import MSRPCHeader
from rpcBase import rpcBase
from formatText import shell_message
config = {}
def main():
parser = argparse.ArgumentParser(description='py2-kms: KMS Server Emulator written in Python2', epilog="version: py2-kms_2018-03-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__ )) + "/py2kms_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.")
shell_message(nshell = [-2, 2])
handler = rpcBind.handler(self.data, config)
elif packetType == rpcBase.packetType['request']:
logging.info("Received activation request.")
shell_message(nshell = [-2, 13])
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.")
shell_message(nshell = [-3, 5, 6])
elif packetType == rpcBase.packetType['request']:
logging.info("Responded to activation request.")
shell_message(nshell = [-3, 18, 19])
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()

756
py2-kms/structure.py Normal file
View file

@ -0,0 +1,756 @@
#!/usr/bin/env python
# Copyright (c) 2003-2016 CORE Security Technologies
#
# This software is provided under under a slightly modified version
# of the Apache Software License. See the accompanying LICENSE file
# for more information.
#
""" Version of https://github.com/CoreSecurity/impacket/blob/master/impacket/structure.py
with modifications in the function dump(...).
Copyright 2018 Matteo Fan <SystemRage@protonmail.com>
"""
from struct import pack, unpack, calcsize
class Structure:
""" sublcasses can define commonHdr and/or structure.
each of them is an tuple of either two: (fieldName, format) or three: (fieldName, ':', class) fields.
[it can't be a dictionary, because order is important]
where format specifies how the data in the field will be converted to/from bytes (string)
class is the class to use when unpacking ':' fields.
each field can only contain one value (or an array of values for *)
i.e. struct.pack('Hl',1,2) is valid, but format specifier 'Hl' is not (you must use 2 dfferent fields)
format specifiers:
specifiers from module pack can be used with the same format
see struct.__doc__ (pack/unpack is finally called)
x [padding byte]
c [character]
b [signed byte]
B [unsigned byte]
h [signed short]
H [unsigned short]
l [signed long]
L [unsigned long]
i [signed integer]
I [unsigned integer]
q [signed long long (quad)]
Q [unsigned long long (quad)]
s [string (array of chars), must be preceded with length in format specifier, padded with zeros]
p [pascal string (includes byte count), must be preceded with length in format specifier, padded with zeros]
f [float]
d [double]
= [native byte ordering, size and alignment]
@ [native byte ordering, standard size and alignment]
! [network byte ordering]
< [little endian]
> [big endian]
usual printf like specifiers can be used (if started with %)
[not recommeneded, there is no why to unpack this]
%08x will output an 8 bytes hex
%s will output a string
%s\\x00 will output a NUL terminated string
%d%d will output 2 decimal digits (against the very same specification of Structure)
...
some additional format specifiers:
: just copy the bytes from the field into the output string (input may be string, other structure, or anything responding to __str__()) (for unpacking, all what's left is returned)
z same as :, but adds a NUL byte at the end (asciiz) (for unpacking the first NUL byte is used as terminator) [asciiz string]
u same as z, but adds two NUL bytes at the end (after padding to an even size with NULs). (same for unpacking) [unicode string]
w DCE-RPC/NDR string (it's a macro for [ '<L=(len(field)+1)/2','"\\x00\\x00\\x00\\x00','<L=(len(field)+1)/2',':' ]
?-field length of field named 'field', formated as specified with ? ('?' may be '!H' for example). The input value overrides the real length
?1*?2 array of elements. Each formated as '?2', the number of elements in the array is stored as specified by '?1' (?1 is optional, or can also be a constant (number), for unpacking)
'xxxx literal xxxx (field's value doesn't change the output. quotes must not be closed or escaped)
"xxxx literal xxxx (field's value doesn't change the output. quotes must not be closed or escaped)
_ will not pack the field. Accepts a third argument, which is an unpack code. See _Test_UnpackCode for an example
?=packcode will evaluate packcode in the context of the structure, and pack the result as specified by ?. Unpacking is made plain
?&fieldname "Address of field fieldname".
For packing it will simply pack the id() of fieldname. Or use 0 if fieldname doesn't exists.
For unpacking, it's used to know weather fieldname has to be unpacked or not, i.e. by adding a & field you turn another field (fieldname) in an optional field.
"""
commonHdr = ()
structure = ()
debug = 0
def __init__(self, data = None, alignment = 0):
if not hasattr(self, 'alignment'):
self.alignment = alignment
self.fields = {}
self.rawData = data
if data is not None:
self.fromString(data)
else:
self.data = None
@classmethod
def fromFile(self, file):
answer = self()
answer.fromString(file.read(len(answer)))
return answer
def setAlignment(self, alignment):
self.alignment = alignment
def setData(self, data):
self.data = data
def packField(self, fieldName, format = None):
if self.debug:
print "packField( %s | %s )" % (fieldName, format)
if format is None:
format = self.formatForField(fieldName)
if self.fields.has_key(fieldName):
ans = self.pack(format, self.fields[fieldName], field = fieldName)
else:
ans = self.pack(format, None, field = fieldName)
if self.debug:
print "\tanswer %r" % ans
return ans
def getData(self):
if self.data is not None:
return self.data
data = ''
for field in self.commonHdr+self.structure:
try:
data += self.packField(field[0], field[1])
except Exception, e:
if self.fields.has_key(field[0]):
e.args += ("When packing field '%s | %s | %r' in %s" % (field[0], field[1], self[field[0]], self.__class__),)
else:
e.args += ("When packing field '%s | %s' in %s" % (field[0], field[1], self.__class__),)
raise
if self.alignment:
if len(data) % self.alignment:
data += ('\x00'*self.alignment)[:-(len(data) % self.alignment)]
#if len(data) % self.alignment: data += ('\x00'*self.alignment)[:-(len(data) % self.alignment)]
return data
def fromString(self, data):
self.rawData = data
for field in self.commonHdr+self.structure:
if self.debug:
print "fromString( %s | %s | %r )" % (field[0], field[1], data)
size = self.calcUnpackSize(field[1], data, field[0])
if self.debug:
print " size = %d" % size
dataClassOrCode = str
if len(field) > 2:
dataClassOrCode = field[2]
try:
self[field[0]] = self.unpack(field[1], data[:size], dataClassOrCode = dataClassOrCode, field = field[0])
except Exception,e:
e.args += ("When unpacking field '%s | %s | %r[:%d]'" % (field[0], field[1], data, size),)
raise
size = self.calcPackSize(field[1], self[field[0]], field[0])
if self.alignment and size % self.alignment:
size += self.alignment - (size % self.alignment)
data = data[size:]
return self
def __setitem__(self, key, value):
self.fields[key] = value
self.data = None # force recompute
def __getitem__(self, key):
return self.fields[key]
def __delitem__(self, key):
del self.fields[key]
def __str__(self):
return self.getData()
def __len__(self):
# XXX: improve
return len(self.getData())
def pack(self, format, data, field = None):
if self.debug:
print " pack( %s | %r | %s)" % (format, data, field)
if field:
addressField = self.findAddressFieldFor(field)
if (addressField is not None) and (data is None):
return ''
# void specifier
if format[:1] == '_':
return ''
# quote specifier
if format[:1] == "'" or format[:1] == '"':
return format[1:]
# code specifier
two = format.split('=')
if len(two) >= 2:
try:
return self.pack(two[0], data)
except:
fields = {'self':self}
fields.update(self.fields)
return self.pack(two[0], eval(two[1], {}, fields))
# address specifier
two = format.split('&')
if len(two) == 2:
try:
return self.pack(two[0], data)
except:
if (self.fields.has_key(two[1])) and (self[two[1]] is not None):
return self.pack(two[0], id(self[two[1]]) & ((1<<(calcsize(two[0])*8))-1) )
else:
return self.pack(two[0], 0)
# length specifier
two = format.split('-')
if len(two) == 2:
try:
return self.pack(two[0],data)
except:
return self.pack(two[0], self.calcPackFieldSize(two[1]))
# array specifier
two = format.split('*')
if len(two) == 2:
answer = ''
for each in data:
answer += self.pack(two[1], each)
if two[0]:
if two[0].isdigit():
if int(two[0]) != len(data):
raise Exception, "Array field has a constant size, and it doesn't match the actual value"
else:
return self.pack(two[0], len(data))+answer
return answer
# "printf" string specifier
if format[:1] == '%':
# format string like specifier
return format % data
# asciiz specifier
if format[:1] == 'z':
return str(data)+'\0'
# unicode specifier
if format[:1] == 'u':
return str(data)+'\0\0' + (len(data) & 1 and '\0' or '')
# DCE-RPC/NDR string specifier
if format[:1] == 'w':
if len(data) == 0:
data = '\0\0'
elif len(data) % 2:
data += '\0'
l = pack('<L', len(data)/2)
return '%s\0\0\0\0%s%s' % (l,l,data)
if data is None:
raise Exception, "Trying to pack None"
# literal specifier
if format[:1] == ':':
return str(data)
# struct like specifier
return pack(format, data)
def unpack(self, format, data, dataClassOrCode = str, field = None):
if self.debug:
print " unpack( %s | %r )" % (format, data)
if field:
addressField = self.findAddressFieldFor(field)
if addressField is not None:
if not self[addressField]:
return
# void specifier
if format[:1] == '_':
if dataClassOrCode != str:
fields = {'self':self, 'inputDataLeft':data}
fields.update(self.fields)
return eval(dataClassOrCode, {}, fields)
else:
return None
# quote specifier
if format[:1] == "'" or format[:1] == '"':
answer = format[1:]
if answer != data:
raise Exception, "Unpacked data doesn't match constant value '%r' should be '%r'" % (data, answer)
return answer
# address specifier
two = format.split('&')
if len(two) == 2:
return self.unpack(two[0],data)
# code specifier
two = format.split('=')
if len(two) >= 2:
return self.unpack(two[0],data)
# length specifier
two = format.split('-')
if len(two) == 2:
return self.unpack(two[0],data)
# array specifier
two = format.split('*')
if len(two) == 2:
answer = []
sofar = 0
if two[0].isdigit():
number = int(two[0])
elif two[0]:
sofar += self.calcUnpackSize(two[0], data)
number = self.unpack(two[0], data[:sofar])
else:
number = -1
while number and sofar < len(data):
nsofar = sofar + self.calcUnpackSize(two[1],data[sofar:])
answer.append(self.unpack(two[1], data[sofar:nsofar], dataClassOrCode))
number -= 1
sofar = nsofar
return answer
# "printf" string specifier
if format[:1] == '%':
# format string like specifier
return format % data
# asciiz specifier
if format == 'z':
if data[-1] != '\x00':
raise Exception, ("%s 'z' field is not NUL terminated: %r" % (field, data))
return data[:-1] # remove trailing NUL
# unicode specifier
if format == 'u':
if data[-2:] != '\x00\x00':
raise Exception, ("%s 'u' field is not NUL-NUL terminated: %r" % (field, data))
return data[:-2] # remove trailing NUL
# DCE-RPC/NDR string specifier
if format == 'w':
l = unpack('<L', data[:4])[0]
return data[12:12+l*2]
# literal specifier
if format == ':':
return dataClassOrCode(data)
# struct like specifier
return unpack(format, data)[0]
def calcPackSize(self, format, data, field = None):
# # print " calcPackSize %s:%r" % (format, data)
if field:
addressField = self.findAddressFieldFor(field)
if addressField is not None:
if not self[addressField]:
return 0
# void specifier
if format[:1] == '_':
return 0
# quote specifier
if format[:1] == "'" or format[:1] == '"':
return len(format)-1
# address specifier
two = format.split('&')
if len(two) == 2:
return self.calcPackSize(two[0], data)
# code specifier
two = format.split('=')
if len(two) >= 2:
return self.calcPackSize(two[0], data)
# length specifier
two = format.split('-')
if len(two) == 2:
return self.calcPackSize(two[0], data)
# array specifier
two = format.split('*')
if len(two) == 2:
answer = 0
if two[0].isdigit():
if int(two[0]) != len(data):
raise Exception, "Array field has a constant size, and it doesn't match the actual value"
elif two[0]:
answer += self.calcPackSize(two[0], len(data))
for each in data:
answer += self.calcPackSize(two[1], each)
return answer
# "printf" string specifier
if format[:1] == '%':
# format string like specifier
return len(format % data)
# asciiz specifier
if format[:1] == 'z':
return len(data)+1
# asciiz specifier
if format[:1] == 'u':
l = len(data)
return l + (l & 1 and 3 or 2)
# DCE-RPC/NDR string specifier
if format[:1] == 'w':
l = len(data)
return 12+l+l % 2
# literal specifier
if format[:1] == ':':
return len(data)
# struct like specifier
return calcsize(format)
def calcUnpackSize(self, format, data, field = None):
if self.debug:
print " calcUnpackSize( %s | %s | %r)" % (field, format, data)
# void specifier
if format[:1] == '_':
return 0
addressField = self.findAddressFieldFor(field)
if addressField is not None:
if not self[addressField]:
return 0
try:
lengthField = self.findLengthFieldFor(field)
return self[lengthField]
except:
pass
# XXX: Try to match to actual values, raise if no match
# quote specifier
if format[:1] == "'" or format[:1] == '"':
return len(format)-1
# address specifier
two = format.split('&')
if len(two) == 2:
return self.calcUnpackSize(two[0], data)
# code specifier
two = format.split('=')
if len(two) >= 2:
return self.calcUnpackSize(two[0], data)
# length specifier
two = format.split('-')
if len(two) == 2:
return self.calcUnpackSize(two[0], data)
# array specifier
two = format.split('*')
if len(two) == 2:
answer = 0
if two[0]:
if two[0].isdigit():
number = int(two[0])
else:
answer += self.calcUnpackSize(two[0], data)
number = self.unpack(two[0], data[:answer])
while number:
number -= 1
answer += self.calcUnpackSize(two[1], data[answer:])
else:
while answer < len(data):
answer += self.calcUnpackSize(two[1], data[answer:])
return answer
# "printf" string specifier
if format[:1] == '%':
raise Exception, "Can't guess the size of a printf like specifier for unpacking"
# asciiz specifier
if format[:1] == 'z':
return data.index('\x00')+1
# asciiz specifier
if format[:1] == 'u':
l = data.index('\x00\x00')
return l + (l & 1 and 3 or 2)
# DCE-RPC/NDR string specifier
if format[:1] == 'w':
l = unpack('<L', data[:4])[0]
return 12+l*2
# literal specifier
if format[:1] == ':':
return len(data)
# struct like specifier
return calcsize(format)
def calcPackFieldSize(self, fieldName, format = None):
if format is None:
format = self.formatForField(fieldName)
return self.calcPackSize(format, self[fieldName])
def formatForField(self, fieldName):
for field in self.commonHdr+self.structure:
if field[0] == fieldName:
return field[1]
raise Exception, ("Field %s not found" % fieldName)
def findAddressFieldFor(self, fieldName):
descriptor = '&%s' % fieldName
l = len(descriptor)
for field in self.commonHdr+self.structure:
if field[1][-l:] == descriptor:
return field[0]
return None
def findLengthFieldFor(self, fieldName):
descriptor = '-%s' % fieldName
l = len(descriptor)
for field in self.commonHdr+self.structure:
if field[1][-l:] == descriptor:
return field[0]
return None
def zeroValue(self, format):
two = format.split('*')
if len(two) == 2:
if two[0].isdigit():
return (self.zeroValue(two[1]),)*int(two[0])
if not format.find('*') == -1: return ()
if 's' in format: return ''
if format in ['z',':','u']: return ''
if format == 'w': return '\x00\x00'
return 0
def clear(self):
for field in self.commonHdr + self.structure:
self[field[0]] = self.zeroValue(field[1])
def dump(self, msg = None, indent = 0, print_to_stdout = True):
if msg is None: msg = self.__class__.__name__
ind = ' '*indent
allstr = "\n%s" % msg
fixedFields = []
for field in self.commonHdr+self.structure:
i = field[0]
if i in self.fields:
fixedFields.append(i)
if isinstance(self[i], Structure):
tempstr = self[i].dump('%s%s:{' % (ind, i), indent = indent + 4, print_to_stdout = False)
allstr += tempstr + "\n%s}" % ind
else:
allstr += "\n%s%s: {%r}" % (ind, i, self[i])
# Do we have remaining fields not defined in the structures? let's
# print them.
remainingFields = list(set(self.fields) - set(fixedFields))
for i in remainingFields:
if isinstance(self[i], Structure):
tempstr = self[i].dump('%s%s:{' % (ind, i), indent = indent + 4, print_to_stdout = False)
allstr += tempstr + "\n%s}" % ind
else:
allstr += "\n%s%s: {%r}" % (ind, i, self[i])
# Finish job.
if not print_to_stdout:
# print allstr # Uncomment this line only for view that test is OK with "print_to_stdout = False".
return allstr
else:
print allstr
class _StructureTest:
alignment = 0
def create(self,data = None):
if data is not None:
return self.theClass(data, alignment = self.alignment)
else:
return self.theClass(alignment = self.alignment)
def run(self):
print
print "-"*70
testName = self.__class__.__name__
print "starting test: %s....." % testName
a = self.create()
self.populate(a)
a.dump("packing.....")
a_str = str(a)
print "packed: %r" % a_str
print "unpacking....."
b = self.create(a_str)
b.dump("unpacked.....")
print "repacking....."
b_str = str(b)
if b_str != a_str:
print "ERROR: original packed and repacked don't match"
print "packed: %r" % b_str
class _Test_simple(_StructureTest):
class theClass(Structure):
commonHdr = ()
structure = (
('int1', '!L'),
('len1','!L-z1'),
('arr1','B*<L'),
('z1', 'z'),
('u1','u'),
('', '"COCA'),
('len2','!H-:1'),
('', '"COCA'),
(':1', ':'),
('int3','>L'),
('code1','>L=len(arr1)*2+0x1000'),
)
def populate(self, a):
a['default'] = 'hola'
a['int1'] = 0x3131
a['int3'] = 0x45444342
a['z1'] = 'hola'
a['u1'] = 'hola'.encode('utf_16_le')
a[':1'] = ':1234:'
a['arr1'] = (0x12341234,0x88990077,0x41414141)
# a['len1'] = 0x42424242
class _Test_fixedLength(_Test_simple):
def populate(self, a):
_Test_simple.populate(self, a)
a['len1'] = 0x42424242
class _Test_simple_aligned4(_Test_simple):
alignment = 4
class _Test_nested(_StructureTest):
class theClass(Structure):
class _Inner(Structure):
structure = (('data', 'z'),)
structure = (
('nest1', ':', _Inner),
('nest2', ':', _Inner),
('int', '<L'),
)
def populate(self, a):
a['nest1'] = _Test_nested.theClass._Inner()
a['nest2'] = _Test_nested.theClass._Inner()
a['nest1']['data'] = 'hola manola'
a['nest2']['data'] = 'chau loco'
a['int'] = 0x12345678
class _Test_Optional(_StructureTest):
class theClass(Structure):
structure = (
('pName','<L&Name'),
('pList','<L&List'),
('Name','w'),
('List','<H*<L'),
)
def populate(self, a):
a['Name'] = 'Optional test'
a['List'] = (1,2,3,4)
class _Test_Optional_sparse(_Test_Optional):
def populate(self, a):
_Test_Optional.populate(self, a)
del a['Name']
class _Test_AsciiZArray(_StructureTest):
class theClass(Structure):
structure = (
('head','<L'),
('array','B*z'),
('tail','<L'),
)
def populate(self, a):
a['head'] = 0x1234
a['tail'] = 0xabcd
a['array'] = ('hola','manola','te traje')
class _Test_UnpackCode(_StructureTest):
class theClass(Structure):
structure = (
('leni','<L=len(uno)*2'),
('cuchi','_-uno','leni/2'),
('uno',':'),
('dos',':'),
)
def populate(self, a):
a['uno'] = 'soy un loco!'
a['dos'] = 'que haces fiera'
class _Test_AAA(_StructureTest):
class theClass(Structure):
commonHdr = ()
structure = (
('iv', '!L=((init_vector & 0xFFFFFF) << 8) | ((pad & 0x3f) << 2) | (keyid & 3)'),
('init_vector', '_','(iv >> 8)'),
('pad', '_','((iv >>2) & 0x3F)'),
('keyid', '_','( iv & 0x03 )'),
('dataLen', '_-data', 'len(inputDataLeft)-4'),
('data',':'),
('icv','>L'),
)
def populate(self, a):
a['init_vector']=0x01020304
#a['pad']=int('01010101',2)
a['pad']=int('010101',2)
a['keyid']=0x07
a['data']="\xA0\xA1\xA2\xA3\xA4\xA5\xA6\xA7\xA8\xA9"
a['icv'] = 0x05060708
#a['iv'] = 0x01020304
if __name__ == '__main__':
_Test_simple().run()
try:
_Test_fixedLength().run()
except:
print "cannot repack because length is bogus"
_Test_simple_aligned4().run()
_Test_nested().run()
_Test_Optional().run()
_Test_Optional_sparse().run()
_Test_AsciiZArray().run()
_Test_UnpackCode().run()
_Test_AAA().run()

202
py2-kms/timezones.py Normal file
View file

@ -0,0 +1,202 @@
#!/usr/bin/env python
# Copyright 2009 Google Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
#
# Disable the invalid name warning as we are inheriting from a standard library
# object.
# pylint: disable-msg=C6409,W0212
"""
Stripped down version of `python-datetime-tz`
that only contains the "find local timezone" bits.
"""
import datetime
import os.path
import time
import warnings
import pytz
# Need to patch pytz.utc to have a _utcoffset so you can normalize/localize
# using it.
pytz.utc._utcoffset = datetime.timedelta()
timedelta = datetime.timedelta
def _tzinfome(tzinfo):
"""Gets a tzinfo object from a string.
Args:
tzinfo: A string (or string like) object, or a datetime.tzinfo object.
Returns:
An datetime.tzinfo object.
Raises:
UnknownTimeZoneError: If the timezone given can't be decoded.
"""
if not isinstance(tzinfo, datetime.tzinfo):
try:
tzinfo = pytz.timezone(tzinfo)
except AttributeError:
raise pytz.UnknownTimeZoneError("Unknown timezone!")
return tzinfo
# Our "local" timezone
_localtz = None
def localtz():
"""Get the local timezone.
Returns:
The localtime timezone as a tzinfo object.
"""
# pylint: disable-msg=W0603
global _localtz
if _localtz is None:
_localtz = detect_timezone()
return _localtz
def detect_timezone():
"""Try and detect the timezone that Python is currently running in.
We have a bunch of different methods for trying to figure this out (listed in
order they are attempted).
* Try and find /etc/timezone file (with timezone name).
* Try and find /etc/localtime file (with timezone data).
* Try and match a TZ to the current dst/offset/shortname.
Returns:
The detected local timezone as a tzinfo object
Raises:
pytz.UnknownTimeZoneError: If it was unable to detect a timezone.
"""
tz = _detect_timezone_etc_timezone()
if tz is not None:
return tz
tz = _detect_timezone_etc_localtime()
if tz is not None:
return tz
# Next we try and use a similiar method to what PHP does.
# We first try to search on time.tzname, time.timezone, time.daylight to
# match a pytz zone.
warnings.warn("Had to fall back to worst detection method (the 'PHP' "
"method).")
tz = _detect_timezone_php()
if tz is not None:
return tz
raise pytz.UnknownTimeZoneError("Unable to detect your timezone!")
def _detect_timezone_etc_timezone():
if os.path.exists("/etc/timezone"):
try:
tz = file("/etc/timezone").read().strip()
try:
return pytz.timezone(tz)
except (IOError, pytz.UnknownTimeZoneError), ei:
warnings.warn("Your /etc/timezone file references a timezone (%r) that"
" is not valid (%r)." % (tz, ei))
# Problem reading the /etc/timezone file
except IOError, eo:
warnings.warn("Could not access your /etc/timezone file: %s" % eo)
def _detect_timezone_etc_localtime():
matches = []
if os.path.exists("/etc/localtime"):
localtime = pytz.tzfile.build_tzinfo("/etc/localtime",
file("/etc/localtime"))
# See if we can find a "Human Name" for this..
for tzname in pytz.all_timezones:
tz = _tzinfome(tzname)
if dir(tz) != dir(localtime):
continue
for attrib in dir(tz):
# Ignore functions and specials
if callable(getattr(tz, attrib)) or attrib.startswith("__"):
continue
# This will always be different
if attrib == "zone" or attrib == "_tzinfos":
continue
if getattr(tz, attrib) != getattr(localtime, attrib):
break
# We get here iff break didn't happen, i.e. no meaningful attributes
# differ between tz and localtime
else:
matches.append(tzname)
#if len(matches) == 1:
# return _tzinfome(matches[0])
#else:
# # Warn the person about this!
# warning = "Could not get a human name for your timezone: "
# if len(matches) > 1:
# warning += ("We detected multiple matches for your /etc/localtime. "
# "(Matches where %s)" % matches)
# else:
# warning += "We detected no matches for your /etc/localtime."
# warnings.warn(warning)
#
# return localtime
if len(matches) > 0:
return _tzinfome(matches[0])
def _detect_timezone_php():
tomatch = (time.tzname[0], time.timezone, time.daylight)
now = datetime.datetime.now()
matches = []
for tzname in pytz.all_timezones:
try:
tz = pytz.timezone(tzname)
except IOError:
continue
try:
indst = tz.localize(now).timetuple()[-1]
if tomatch == (tz._tzname, -tz._utcoffset.seconds, indst):
matches.append(tzname)
# pylint: disable-msg=W0704
except AttributeError:
pass
if len(matches) > 1:
warnings.warn("We detected multiple matches for the timezone, choosing "
"the first %s. (Matches where %s)" % (matches[0], matches))
return pytz.timezone(matches[0])