mirror of
https://github.com/SystemRage/py-kms.git
synced 2024-11-22 08:15:38 +01:00
py2-kms upload
This commit is contained in:
parent
1d038f22b5
commit
0974c3e33a
710
py2-kms/aes.py
Normal file
710
py2-kms/aes.py
Normal 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
260
py2-kms/client.py
Normal 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
365
py2-kms/dcerpc.py
Normal 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
106
py2-kms/filetimes.py
Normal 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
96
py2-kms/formatText.py
Normal 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
628
py2-kms/kmsBase.py
Normal 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
136
py2-kms/kmsPidGenerator.py
Normal 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)
|
13
py2-kms/kmsRequestUnknown.py
Normal file
13
py2-kms/kmsRequestUnknown.py
Normal 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
125
py2-kms/kmsRequestV4.py
Normal 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
175
py2-kms/kmsRequestV5.py
Normal 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
108
py2-kms/kmsRequestV6.py
Normal 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
6
py2-kms/randomHWID.py
Normal 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
37
py2-kms/randomPID.py
Normal 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
62
py2-kms/rpcBase.py
Normal 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
169
py2-kms/rpcBind.py
Normal 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
67
py2-kms/rpcRequest.py
Normal 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
134
py2-kms/server.py
Normal 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
756
py2-kms/structure.py
Normal 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
202
py2-kms/timezones.py
Normal 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])
|
||||
|
Loading…
Reference in New Issue
Block a user