mirror of
https://github.com/fortra/impacket.git
synced 2026-11-07 09:26:09 -10:00
MS-FSRVP, MS-SCMR (#2265)
* Fix some SMB relay server syntax error * Added MS-FSRVP, 2 new calls to SCMR * Remove wrong email * added tests for MS-FSRVP * fix some errors with vairable typing * Added remote mark for test FSRVP * Fix FSRVP type hints for Python 3.9 * Add offline NDR tests for FSRVP and SCMR * Expand FSRVP and SCMR remote tests and skip unsupported methods * Fix SCMR service creation response tag pointer types --------- Co-authored-by: Gabi Gonzalez <[email protected]>
This commit is contained in:
1 parent
ba33c7ed5f
commit
505d39ec39
5 files changed
+832
-7
No files matched your search
+70
-4
@@ -95,8 +95,10 @@ Active Directory Setup and Configuration
|
||||
|
||||
In order to run remote test cases, a target Active Directory need to be properly
|
||||
configured with the expected objects. Current remote test cases are expected to
|
||||
work against a Windows Server 2012 R2 Domain Controller. The following are the
|
||||
main steps required:
|
||||
work against a Windows Server 2012 R2 Domain Controller. Some protocol tests need
|
||||
additional server roles or newer Windows versions; see
|
||||
[Protocol-specific remote test prerequisites](#protocol-specific-remote-test-prerequisites).
|
||||
The following are the main steps required:
|
||||
|
||||
1. Make sure to disable the firewall on the interface you want to use for connecting
|
||||
to the Domain Controller.
|
||||
@@ -231,7 +233,7 @@ do that by running Mimikatz with an elevated user and executing:
|
||||
Configure Remote Test Cases
|
||||
---------------------------
|
||||
|
||||
Create a copy of the [dcetest.cfg.template](tests/dcetests.cfg.template) file and
|
||||
Create a copy of the [dcetests.cfg.template](tests/dcetests.cfg.template) file and
|
||||
configure it with the necessary information associated to the Active Directory you
|
||||
configured. Path to the configuration file to use when running tests can be then
|
||||
specified in the following ways:
|
||||
@@ -239,7 +241,7 @@ specified in the following ways:
|
||||
* Using the pytest `--remote-config` command-line option.
|
||||
* Using the pytest `remote-config` option in `tox.ini`.
|
||||
* Using the `REMOTE_CONFIG` environment variable.
|
||||
* Default to loading from `tests/dcetests.cg`.
|
||||
* Default to loading from `tests/dcetests.cfg`.
|
||||
|
||||
For example, you can keep configuration of different environments in
|
||||
separate files, and specify which one you want the test to run against:
|
||||
@@ -257,3 +259,67 @@ resolve DNS queries for the Active Directory Domain configured. If you don't wan
|
||||
change your test machine's DNS settings to point to the AD DNS server, you can
|
||||
configure your system to statically resolve (e.g. via `/etc/hosts` file) the host
|
||||
and domain FQDN to the server's IP address.
|
||||
|
||||
|
||||
Protocol-specific remote test prerequisites
|
||||
------------------------------------------
|
||||
|
||||
Some RPC interfaces require additional Windows roles or a newer operating system
|
||||
than the Windows Server 2012 R2 environment described above. Configure the target
|
||||
for each interface as follows, then specify its connection information as
|
||||
described in [Configure Remote Test Cases](#configure-remote-test-cases).
|
||||
|
||||
### SCMR: RCreateWowService and ROpenSCManager2
|
||||
|
||||
For the `RCreateWowService` and `ROpenSCManager2` cases in
|
||||
[test_scmr.py](tests/dcerpc/test_scmr.py):
|
||||
|
||||
These cases skip automatically when the target reports an unsupported RPC
|
||||
operation or `ERROR_CALL_NOT_IMPLEMENTED`. Other errors, including access denied
|
||||
and connection failures, still fail the tests. No version flag is required.
|
||||
|
||||
1. Prepare a Windows host supporting these methods, such as Windows 11.
|
||||
A domain controller is not required for these cases. Refer to the
|
||||
[MS-SCMR product behavior notes](https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-scmr/041d2a89-9d7d-4f79-91a1-c336d0a668f6)
|
||||
for version restrictions. In particular, `ROpenSCManager2` is unavailable on
|
||||
Windows 10 version 1809 and earlier and Windows Server version 1809 and earlier.
|
||||
|
||||
1. Configure an administrator account with network logon access and permission
|
||||
to open the SCM database and create, query, and delete services.
|
||||
|
||||
1. Allow SMB connections on TCP 445. For the TCP transport cases, also allow
|
||||
TCP 135 and the dynamic RPC ports used by the server.
|
||||
|
||||
1. Set the account and target details in the remote configuration file's
|
||||
`[TCPTransport]` section, which is used by both transport classes. For a local
|
||||
account on a standalone host, set `domain` to the target computer name and
|
||||
`username` to the local account name.
|
||||
|
||||
### FSRVP: file-server shadow copies
|
||||
|
||||
For [test_fsrvp.py](tests/dcerpc/test_fsrvp.py), prepare a Windows Server with
|
||||
storage that supports shadow copies through FSRVP. Run the following setup steps
|
||||
in an elevated PowerShell session on the target:
|
||||
|
||||
1. Install the File Server VSS Agent Service role service. Complete any restart
|
||||
requested by the installation before continuing.
|
||||
|
||||
PS C:\> Install-WindowsFeature -Name File-Services,FS-VSS-Agent
|
||||
|
||||
1. Enable and start the `FSSAgent` service.
|
||||
|
||||
PS C:\> Set-Service -Name FSSAgent -StartupType Automatic
|
||||
PS C:\> Start-Service -Name FSSAgent
|
||||
|
||||
1. Create a regular SMB share named `Windows`, matching the `SHARE_NAME` constant
|
||||
in the test module. Administrative shares such as `C$` cannot be used. For
|
||||
example, create a test directory and share it with the test administrator:
|
||||
|
||||
PS C:\> New-Item -Path C:\FSRVPTest -ItemType Directory
|
||||
PS C:\> New-SmbShare -Name Windows -Path C:\FSRVPTest -FullAccess '<Domain>\<Admin User Name>'
|
||||
|
||||
1. Ensure the test administrator has access to both the share and its underlying
|
||||
directory, and allow SMB connections on TCP 445.
|
||||
|
||||
1. Configure the target address and administrator credentials in the remote
|
||||
test configuration file.
|
||||
@@ -0,0 +1,442 @@
|
||||
# Impacket - Collection of Python classes for working with network protocols.
|
||||
#
|
||||
# Copyright Fortra, LLC and its affiliated companies
|
||||
#
|
||||
# All rights reserved.
|
||||
#
|
||||
# This software is provided under a slightly modified version
|
||||
# of the Apache Software License. See the accompanying LICENSE file
|
||||
# for more information.
|
||||
#
|
||||
# Description:
|
||||
# Implementation of MS-FSRVP: File server remote VSS Protocol.
|
||||
#
|
||||
# Authors:
|
||||
# Andrei Solodiankin https://github.com/AndreySolod
|
||||
#
|
||||
|
||||
|
||||
from __future__ import division
|
||||
from __future__ import print_function
|
||||
import enum
|
||||
from typing import Optional, Union
|
||||
|
||||
from impacket import nt_errors
|
||||
from impacket.dcerpc.v5.dtypes import LONG, DWORD, WSTR, LPWSTR, ULONG, GUID, LONGLONG, BOOL
|
||||
from impacket.dcerpc.v5.ndr import NDRCALL, NDRPOINTER, NDRSTRUCT, NULL, NDRUNION
|
||||
from impacket.dcerpc.v5.rpcrt import DCERPCException, DCERPC_v5
|
||||
from impacket.uuid import uuidtup_to_bin, string_to_bin
|
||||
|
||||
MSRPC_UUID_FSRVP = uuidtup_to_bin(('a8e0653c-2744-4389-a61d-7373df8b2292', '1.0'))
|
||||
MSRPC_NAMED_PIPE_FSRVP = "\\pipe\\FssagentRpc"
|
||||
|
||||
FSRVP_ERROR_MESSAGES = {
|
||||
0x80042301 : ("FSRVP_E_BAD_STATE", "A method call was invalid because of the state of the server. (For example, calling AddToShadowCopySet (Opnum 3) before StartShadowCopySet (Opnum 2).)"),
|
||||
0x80042316 : ("FSRVP_E_SHADOW_COPY_SET_IN_PROGRESS", "A call was made to either SetContext (Opnum 1) or StartShadowCopySet (Opnum 2) while the creation of another shadow copy set is in progress."),
|
||||
0x8004230C : ("FSRVP_E_NOT_SUPPORTED", "The file store which contains the share to be shadow copied is not supported by the server."),
|
||||
0xFFFFFFFF : ("FSRVP_E_WAIT_FAILED", "The wait for a shadow copy commit expose operation has failed."),
|
||||
0x8004230D : ("FSRVP_E_OBJECT_ALREADY_EXISTS", "The specified object already exists."),
|
||||
0x80042308 : ("FSRVP_E_OBJECT_NOT_FOUND", "The specified object does not exist."),
|
||||
0x8004231B : ("FSRVP_E_UNSUPPORTED_CONTEXT", "The specified context value is invalid"),
|
||||
0x80042501 : ("FSRVP_E_SHADOWCOPYSET_ID_MISMATCH", "The provided ShadowCopySetId does not exist."),
|
||||
0x80070005 : ("E_ACCESSDENIED", "The caller does not have the permissions to perform the operation"),
|
||||
0x80070057 : ("E_INVALIDARG", "One or more arguments are invalid.")
|
||||
}
|
||||
|
||||
class DCERPCSessionError(DCERPCException):
|
||||
def __init__(self, error_string=None, error_code=None, packet=None):
|
||||
DCERPCException.__init__(self, error_string, error_code, packet)
|
||||
|
||||
def __str__( self ):
|
||||
key = self.error_code
|
||||
if key in FSRVP_ERROR_MESSAGES:
|
||||
error_msg_short = FSRVP_ERROR_MESSAGES[key][0]
|
||||
error_msg_verbose = FSRVP_ERROR_MESSAGES[key][1]
|
||||
return 'FSRVP SessionError: code: 0x%x - %s - %s' % (self.error_code, error_msg_short, error_msg_verbose)
|
||||
if key in nt_errors.ERROR_MESSAGES:
|
||||
error_msg_short = nt_errors.ERROR_MESSAGES[key][0]
|
||||
error_msg_verbose = nt_errors.ERROR_MESSAGES[key][1]
|
||||
return 'FSRVP SessionError: code: 0x%x - %s - %s' % (self.error_code, error_msg_short, error_msg_verbose)
|
||||
else:
|
||||
return 'FSRVP SessionError: unknown error code: 0x%x' % self.error_code
|
||||
|
||||
################################################################################
|
||||
# CONSTANTS
|
||||
################################################################################
|
||||
|
||||
class ShadowCopyAttributes(enum.IntEnum):
|
||||
ATTR_PERSISTENT = 0x00000001
|
||||
ATTR_NO_AUTO_RECOVERY = 0x00000002
|
||||
ATTR_NO_AUTO_RELEASE = 0x00000008
|
||||
ATTR_NO_WRITERS = 0x00000010
|
||||
ATTR_AUTO_RECOVERY = 0x00400000
|
||||
|
||||
|
||||
class ContextValues(enum.IntEnum):
|
||||
CTX_BACKUP = 0x00000000
|
||||
CTX_FILE_SHARE_BACKUP = 0x00000010
|
||||
CTX_NAS_ROLLBACK = 0x00000019
|
||||
CTX_APP_ROLLBACK = 0x00000009
|
||||
|
||||
|
||||
class ShadowCopyCompatibilityValues(enum.IntEnum):
|
||||
DISABLE_DEFRAG = 0x00000001
|
||||
DISABLE_CONTENTINDEX = 0x00000002
|
||||
|
||||
################################################################################
|
||||
# STRUCTURES
|
||||
################################################################################
|
||||
class FSSAGENT_SHARE_MAPPING_1(NDRSTRUCT):
|
||||
structure = (
|
||||
('ShadowCopySetId', GUID),
|
||||
('ShadowCopyId', GUID),
|
||||
('ShareNameUNC', LPWSTR),
|
||||
('ShadowCopyShareName', LPWSTR),
|
||||
('CreationTimestamp', LONGLONG),
|
||||
)
|
||||
|
||||
class PFSSAGENT_SHARE_MAPPING_1(NDRPOINTER):
|
||||
referent = (
|
||||
('Data', FSSAGENT_SHARE_MAPPING_1),
|
||||
)
|
||||
|
||||
class FSSAGENT_SHARE_MAPPING(NDRUNION):
|
||||
commonHdr = (
|
||||
('tag', DWORD),
|
||||
)
|
||||
|
||||
union = {
|
||||
1: ('ShareMapping1', PFSSAGENT_SHARE_MAPPING_1),
|
||||
}
|
||||
|
||||
class PFSSAGENT_SHARE_MAPPING(NDRPOINTER):
|
||||
referent = (
|
||||
('Data', FSSAGENT_SHARE_MAPPING),
|
||||
)
|
||||
|
||||
################################################################################
|
||||
# RPC CALLS
|
||||
################################################################################
|
||||
class GetSupportedVersion(NDRCALL):
|
||||
'''The GetSupportedVersion method is invoked by the client to get the minimum and maximum versions of the protocol that the server supports.'''
|
||||
opnum = 0
|
||||
structure = tuple()
|
||||
|
||||
|
||||
class GetSupportedVersionResponse(NDRCALL):
|
||||
structure = (
|
||||
('MinVersion', DWORD),
|
||||
('MaxVersion', DWORD),
|
||||
('ErrorCode',DWORD),
|
||||
)
|
||||
|
||||
|
||||
class SetContext(NDRCALL):
|
||||
'''The SetContext method sets the context for the current shadow copy creation process.'''
|
||||
opnum = 1
|
||||
structure = (
|
||||
('Context', ULONG),
|
||||
)
|
||||
|
||||
|
||||
class SetContextResponse(NDRCALL):
|
||||
structure = (
|
||||
('ErrorCode', DWORD),
|
||||
)
|
||||
|
||||
|
||||
class StartShadowCopySet(NDRCALL):
|
||||
'''The StartShadowCopySet method is called by the client to initiate a new shadow copy set for shadow copy creation'''
|
||||
opnum = 2
|
||||
structure = (
|
||||
('ClientShadowCopySetId', GUID),
|
||||
)
|
||||
|
||||
|
||||
class StartShadowCopySetResponse(NDRCALL):
|
||||
structure = (
|
||||
('pShadowCopySetId', GUID),
|
||||
('ErrorCode', DWORD)
|
||||
)
|
||||
|
||||
|
||||
class AddToShadowCopySet(NDRCALL):
|
||||
'''The AddToShadowCopySet method adds a share to an existing shadow copy set.'''
|
||||
opnum = 3
|
||||
structure = (
|
||||
('ClientShadowCopyId', GUID),
|
||||
('ShadowCopySetId', GUID),
|
||||
('ShareName', WSTR)
|
||||
)
|
||||
|
||||
|
||||
class AddToShadowCopySetResponse(NDRCALL):
|
||||
structure = (
|
||||
("ShadowCopyId", GUID),
|
||||
("ErrorCode", DWORD)
|
||||
)
|
||||
|
||||
|
||||
class CommitShadowCopySet(NDRCALL):
|
||||
'''The CommitShadowCopySet method is invoked by the client to commit a given shadow copy set.'''
|
||||
opnum = 4
|
||||
structure = (
|
||||
('ShadowCopySetId', GUID),
|
||||
('TimeOutInMilliseconds', ULONG)
|
||||
)
|
||||
|
||||
|
||||
class CommitShadowCopySetResponse(NDRCALL):
|
||||
structure = (
|
||||
("ErrorCode", DWORD),
|
||||
)
|
||||
|
||||
|
||||
class ExposeShadowCopySet(NDRCALL):
|
||||
'''The ExposeShadowCopySet method exposes all the shadow copies in a shadow copy set as file shares on the file server.'''
|
||||
opnum = 5
|
||||
structure = (
|
||||
("ShadowCopySetId", GUID),
|
||||
('TimeOutInMilliseconds', ULONG)
|
||||
)
|
||||
|
||||
|
||||
class ExposeShadowCopySetResponse(NDRCALL):
|
||||
structure = (
|
||||
("ErrorCode", DWORD),
|
||||
)
|
||||
|
||||
|
||||
class RecoveryCompleteShadowCopySet(NDRCALL):
|
||||
'''The RecoveryCompleteShadowCopySet method is invoked by the client to indicate to the server
|
||||
that the data associated with the file shares in a shadow copy set have been recovered by the VSS
|
||||
writers.
|
||||
'''
|
||||
opnum = 6
|
||||
structure = (
|
||||
("ShadowCopySetId", GUID),
|
||||
)
|
||||
|
||||
|
||||
class RecoveryCompleteShadowCopySetResponse(NDRCALL):
|
||||
structure = (
|
||||
("ErrorCode", DWORD),
|
||||
)
|
||||
|
||||
|
||||
class AbortShadowCopySet(NDRCALL):
|
||||
'''The AbortShadowCopySet method is invoked by the client to delete a given shadow copy set on the server.'''
|
||||
opnum = 7
|
||||
structure = (
|
||||
("ShadowCopySetId", GUID),
|
||||
)
|
||||
|
||||
|
||||
class AbortShadowCopySetResponse(NDRCALL):
|
||||
structure = (
|
||||
("ErrorCode", DWORD),
|
||||
)
|
||||
|
||||
|
||||
class IsPathSupported(NDRCALL):
|
||||
'''The IsPathSupported method is invoked by the client to query if a given share is supported by the server for shadow copy operations.'''
|
||||
opnum = 8
|
||||
structure = (
|
||||
("ShareName", WSTR),
|
||||
)
|
||||
|
||||
|
||||
class IsPathSupportedResponse(NDRCALL):
|
||||
structure = (
|
||||
('SupportedByThisProvider', BOOL),
|
||||
('OwnerMachineName', LPWSTR),
|
||||
('ErrorCode', DWORD)
|
||||
)
|
||||
|
||||
class IsPathShadowCopied(NDRCALL):
|
||||
'''The IsPathShadowCopied method is invoked by the client to query if any shadow copy for a share already exists.'''
|
||||
opnum = 9
|
||||
structure = (
|
||||
("ShareName", WSTR),
|
||||
)
|
||||
|
||||
|
||||
class IsPathShadowCopiedResponse(NDRCALL):
|
||||
structure = (
|
||||
("ShadowCopyPresent", BOOL),
|
||||
("ShadowCopyCompatibility", LONG),
|
||||
('ErrorCode', DWORD)
|
||||
)
|
||||
|
||||
|
||||
class GetShareMapping(NDRCALL):
|
||||
'''The GetShareMapping method is invoked by the client to get the shadow copy information on a
|
||||
given file share on the server after the shadow copy of the share has been exposed.'''
|
||||
opnum = 10
|
||||
structure = (
|
||||
("ShadowCopyId", GUID),
|
||||
("ShadowCopySetId", GUID),
|
||||
("ShareName", WSTR),
|
||||
("Level", DWORD)
|
||||
)
|
||||
|
||||
|
||||
class GetShareMappingResponse(NDRCALL):
|
||||
structure = (
|
||||
('ShareMapping', FSSAGENT_SHARE_MAPPING),
|
||||
('ErrorCode', DWORD),
|
||||
)
|
||||
|
||||
|
||||
class DeleteShareMapping(NDRCALL):
|
||||
'''The DeleteShareMapping method deletes the mapping of a share's shadow copy from a shadow copy set. '''
|
||||
opnum = 11
|
||||
structure = (
|
||||
('ShadowCopySetId', GUID),
|
||||
('ShadowCopyId', GUID),
|
||||
('ShareName', WSTR)
|
||||
)
|
||||
|
||||
|
||||
class DeleteShareMappingResponse(NDRCALL):
|
||||
structure = (
|
||||
('ErrorCode', DWORD),
|
||||
)
|
||||
|
||||
|
||||
class PrepareShadowCopySet(NDRCALL):
|
||||
'''The PrepareShadowCopySet method is invoked by the client to ensure that the server has
|
||||
completed preparation for creating the shadow copy set.'''
|
||||
opnum = 12
|
||||
structure = (
|
||||
('ShadowCopySetId', GUID),
|
||||
('TimeOutInMilliseconds', ULONG)
|
||||
)
|
||||
|
||||
|
||||
class PrepareShadowCopySetResponse(NDRCALL):
|
||||
structure = (
|
||||
('ErrorCode', DWORD),
|
||||
)
|
||||
|
||||
################################################################################
|
||||
# OPNUMs and their corresponding structures
|
||||
################################################################################
|
||||
OPNUMS = {
|
||||
0 : (GetSupportedVersion, GetSupportedVersionResponse),
|
||||
1 : (SetContext, SetContextResponse),
|
||||
2 : (StartShadowCopySet, StartShadowCopySetResponse),
|
||||
3 : (AddToShadowCopySet, AddToShadowCopySetResponse),
|
||||
4 : (CommitShadowCopySet, CommitShadowCopySetResponse),
|
||||
5 : (ExposeShadowCopySet, ExposeShadowCopySetResponse),
|
||||
6 : (RecoveryCompleteShadowCopySet, RecoveryCompleteShadowCopySetResponse),
|
||||
7 : (AbortShadowCopySet, AbortShadowCopySetResponse),
|
||||
8 : (IsPathSupported, IsPathSupportedResponse),
|
||||
9 : (IsPathShadowCopied, IsPathShadowCopiedResponse),
|
||||
10 : (GetShareMapping, GetShareMappingResponse),
|
||||
11 : (DeleteShareMapping, DeleteShareMappingResponse),
|
||||
12 : (PrepareShadowCopySet, PrepareShadowCopySetResponse)
|
||||
}
|
||||
|
||||
################################################################################
|
||||
# HELPER FUNCTIONS
|
||||
################################################################################
|
||||
def checkNullString(string):
|
||||
if string == NULL:
|
||||
return string
|
||||
|
||||
if string is None:
|
||||
return NULL
|
||||
|
||||
if string[-1:] != '\x00':
|
||||
return string + '\x00'
|
||||
else:
|
||||
return string
|
||||
|
||||
|
||||
def hGetSupportedVersion(dce: DCERPC_v5) -> GetSupportedVersionResponse:
|
||||
gsv = GetSupportedVersion()
|
||||
return dce.request(gsv)
|
||||
|
||||
|
||||
def hSetContext(dce: DCERPC_v5, context: Union[ShadowCopyAttributes, int]) -> SetContextResponse:
|
||||
set_context = SetContext()
|
||||
ctx = context if isinstance(context, int) else context.value
|
||||
set_context['Context'] = ctx
|
||||
return dce.request(set_context)
|
||||
|
||||
|
||||
def hStartShadowCopySet(dce: DCERPC_v5, clientShadowCopySetId: bytes) -> StartShadowCopySetResponse:
|
||||
start_shadow_copy = StartShadowCopySet()
|
||||
start_shadow_copy['ClientShadowCopySetId'] = clientShadowCopySetId
|
||||
return dce.request(start_shadow_copy)
|
||||
|
||||
|
||||
def hAddToShadowCopySet(dce: DCERPC_v5, ClientShadowCopyId: bytes, ShadowCopySetId: bytes, ShareName: str) -> AddToShadowCopySetResponse:
|
||||
add_to_shadow_copy = AddToShadowCopySet()
|
||||
add_to_shadow_copy['ClientShadowCopyId'] = ClientShadowCopyId
|
||||
add_to_shadow_copy['ShadowCopySetId'] = ShadowCopySetId
|
||||
add_to_shadow_copy['ShareName'] = checkNullString(ShareName)
|
||||
return dce.request(add_to_shadow_copy)
|
||||
|
||||
|
||||
def hCommitShadowCopySet(dce: DCERPC_v5, ShadowCopySetId: str, TimeOutInMilliseconds: int) -> CommitShadowCopySetResponse:
|
||||
commit_shadow_copy = CommitShadowCopySet()
|
||||
commit_shadow_copy['ShadowCopySetId'] = ShadowCopySetId
|
||||
commit_shadow_copy['TimeOutInMilliseconds'] = TimeOutInMilliseconds
|
||||
return dce.request(commit_shadow_copy)
|
||||
|
||||
|
||||
def hExposeShadowCopySet(dce: DCERPC_v5, ShadowCopySetId: bytes, TimeOutInMilliseconds: int) -> ExposeShadowCopySetResponse:
|
||||
expose_shadow_copy = ExposeShadowCopySet()
|
||||
expose_shadow_copy['ShadowCopySetId'] = ShadowCopySetId
|
||||
expose_shadow_copy['TimeOutInMilliseconds'] = TimeOutInMilliseconds
|
||||
return dce.request(expose_shadow_copy)
|
||||
|
||||
|
||||
def hRecoveryCompleteShadowCopySet(dce: DCERPC_v5, ShadowCopySetId: bytes) -> RecoveryCompleteShadowCopySetResponse:
|
||||
recovery_complete = RecoveryCompleteShadowCopySet()
|
||||
recovery_complete['ShadowCopySetId'] = ShadowCopySetId
|
||||
return dce.request(recovery_complete)
|
||||
|
||||
|
||||
def hAbortShadowCopySet(dce: DCERPC_v5, ShadowCopySetId: bytes) -> AbortShadowCopySetResponse:
|
||||
request = AbortShadowCopySet()
|
||||
request['ShadowCopySetId'] = ShadowCopySetId
|
||||
return dce.request(request)
|
||||
|
||||
|
||||
def hIsPathSupported(dce: DCERPC_v5, ShareName: Optional[str]) -> IsPathSupportedResponse:
|
||||
request = IsPathSupported()
|
||||
request['ShareName'] = checkNullString(ShareName)
|
||||
return dce.request(request)
|
||||
|
||||
|
||||
def hIsPathShadowCopied(dce: DCERPC_v5, ShareName: Optional[str]) -> IsPathShadowCopiedResponse:
|
||||
request = IsPathShadowCopied()
|
||||
request['ShareName'] = checkNullString(ShareName)
|
||||
return dce.request(request)
|
||||
|
||||
|
||||
def hGetShareMapping(dce: DCERPC_v5, ShadowCopyId: bytes, ShadowCopySetId: bytes, ShareName: str, Level: int=1) -> GetShareMappingResponse:
|
||||
request = GetShareMapping()
|
||||
request['ShadowCopyId'] = ShadowCopyId
|
||||
request['ShadowCopySetId'] = ShadowCopySetId
|
||||
request['ShareName'] = checkNullString(ShareName)
|
||||
request['Level'] = Level
|
||||
return dce.request(request)
|
||||
|
||||
|
||||
def hDeleteShareMapping(dce: DCERPC_v5, ShadowCopySetId: bytes, ShadowCopyId: bytes, ShareName: str) -> DeleteShareMappingResponse:
|
||||
request = DeleteShareMapping()
|
||||
request['ShadowCopySetId'] = ShadowCopySetId
|
||||
request['ShadowCopyId'] = ShadowCopyId
|
||||
request['ShareName'] = checkNullString(ShareName)
|
||||
return dce.request(request)
|
||||
|
||||
|
||||
def hPrepareShadowCopySet(dce: DCERPC_v5, ShadowCopySetId: bytes, TimeOutInMilliseconds: int) -> PrepareShadowCopySetResponse:
|
||||
request = PrepareShadowCopySet()
|
||||
request['ShadowCopySetId'] = ShadowCopySetId
|
||||
request['TimeOutInMilliseconds'] = TimeOutInMilliseconds
|
||||
return dce.request(request)
|
||||
+112
-2
@@ -209,6 +209,41 @@ SERVICE_TRIGGER_DATA_TYPE_LEVEL = 0x00000003
|
||||
SERVICE_TRIGGER_DATA_TYPE_KEYWORD_ANY = 0x00000004
|
||||
SERVICE_TRIGGER_DATA_TYPE_KEYWORD_ALL = 0x00000005
|
||||
|
||||
# RCreateWowService dwServiceWowType dataTypes (https://learn.microsoft.com/ko-kr/openspecs/windows_protocols/ms-scmr/b0d95ad9-30f3-4df0-998d-d43fe8ea580c#Appendix_A_Target_84)
|
||||
IMAGE_FILE_MACHINE_UNKNOWN = 0x0
|
||||
IMAGE_FILE_MACHINE_TARGET_HOST = 0x0001
|
||||
IMAGE_FILE_MACHINE_I386 = 0x014c
|
||||
IMAGE_FILE_MACHINE_R3000BE = 0x0160
|
||||
IMAGE_FILE_MACHINE_R3000 = 0x0162
|
||||
IMAGE_FILE_MACHINE_R4000 = 0x0166
|
||||
IMAGE_FILE_MACHINE_R10000 = 0x0168
|
||||
IMAGE_FILE_MACHINE_WCEMIPSV2 = 0x0169
|
||||
IMAGE_FILE_MACHINE_ALPHA = 0x0184
|
||||
IMAGE_FILE_MACHINE_SH3 = 0x01a2
|
||||
IMAGE_FILE_MACHINE_SH3DSP = 0x01a3
|
||||
IMAGE_FILE_MACHINE_SH3E = 0x01a4
|
||||
IMAGE_FILE_MACHINE_SH4 = 0x01a6
|
||||
IMAGE_FILE_MACHINE_SH5 = 0x01a8
|
||||
IMAGE_FILE_MACHINE_ARM = 0x01c0
|
||||
IMAGE_FILE_MACHINE_THUMB = 0x01c2
|
||||
IMAGE_FILE_MACHINE_ARMNT = 0x01c4
|
||||
IMAGE_FILE_MACHINE_AM33 = 0x01d3
|
||||
IMAGE_FILE_MACHINE_POWERPC = 0x01F0
|
||||
IMAGE_FILE_MACHINE_POWERPCFP = 0x01f1
|
||||
IMAGE_FILE_MACHINE_IA64 = 0x0200
|
||||
IMAGE_FILE_MACHINE_MIPS16 = 0x0266
|
||||
IMAGE_FILE_MACHINE_ALPHA64 = 0x0284
|
||||
IMAGE_FILE_MACHINE_MIPSFPU = 0x0366
|
||||
IMAGE_FILE_MACHINE_MIPSFPU16 = 0x0466
|
||||
IMAGE_FILE_MACHINE_AXP64 = 0x0284
|
||||
IMAGE_FILE_MACHINE_TRICORE = 0x0520
|
||||
IMAGE_FILE_MACHINE_CEF = 0x0CEF
|
||||
IMAGE_FILE_MACHINE_EBC = 0x0EBC
|
||||
IMAGE_FILE_MACHINE_AMD64 = 0x8664
|
||||
IMAGE_FILE_MACHINE_M32R = 0x9041
|
||||
IMAGE_FILE_MACHINE_ARM64 = 0xAA64
|
||||
IMAGE_FILE_MACHINE_CEE = 0xC0EE
|
||||
|
||||
################################################################################
|
||||
# STRUCTURES
|
||||
################################################################################
|
||||
@@ -811,7 +846,7 @@ class RCreateServiceW(NDRCALL):
|
||||
|
||||
class RCreateServiceWResponse(NDRCALL):
|
||||
structure = (
|
||||
('lpdwTagId',LPWSTR),
|
||||
('lpdwTagId',LPDWORD),
|
||||
('lpServiceHandle',SC_RPC_HANDLE),
|
||||
('ErrorCode', DWORD),
|
||||
)
|
||||
@@ -1055,7 +1090,7 @@ class RCreateServiceWOW64W(NDRCALL):
|
||||
|
||||
class RCreateServiceWOW64WResponse(NDRCALL):
|
||||
structure = (
|
||||
('lpdwTagId',LPWSTR),
|
||||
('lpdwTagId',LPDWORD),
|
||||
('lpServiceHandle',SC_RPC_HANDLE),
|
||||
('ErrorCode', DWORD),
|
||||
)
|
||||
@@ -1133,6 +1168,49 @@ class RQueryServiceConfigExResponse(NDRCALL):
|
||||
('ErrorCode', DWORD),
|
||||
)
|
||||
|
||||
class RCreateWowService(NDRCALL):
|
||||
opnum = 60
|
||||
structure = (
|
||||
('hSCManager',SC_RPC_HANDLE),
|
||||
('lpServiceName',WSTR),
|
||||
('lpDisplayName',LPWSTR),
|
||||
('dwDesiredAccess',DWORD),
|
||||
('dwServiceType',DWORD),
|
||||
('dwStartType',DWORD),
|
||||
('dwErrorControl',DWORD),
|
||||
('lpBinaryPathName',WSTR),
|
||||
('lpLoadOrderGroup',LPWSTR),
|
||||
('lpdwTagId',LPDWORD),
|
||||
('lpDependencies',LPBYTE),
|
||||
('dwDependSize',DWORD),
|
||||
('lpServiceStartName',LPWSTR),
|
||||
('lpPassword',LPBYTE),
|
||||
('dwPwSize',DWORD),
|
||||
('dwServiceWowType',USHORT),
|
||||
)
|
||||
|
||||
|
||||
class RCreateWowServiceResponse(NDRCALL):
|
||||
structure = (
|
||||
('lpdwTagId',LPDWORD),
|
||||
('lpServiceHandle',SC_RPC_HANDLE),
|
||||
('ErrorCode', DWORD),
|
||||
)
|
||||
|
||||
|
||||
class ROpenSCManager2(NDRCALL):
|
||||
opnum = 64
|
||||
structure = (
|
||||
('lpDatabaseName', LPWSTR),
|
||||
('dwDesiredAccess', DWORD),
|
||||
)
|
||||
|
||||
class ROpenSCManager2Response(NDRCALL):
|
||||
structure = (
|
||||
('lpScHandle', SC_RPC_HANDLE),
|
||||
('ReturnCode', DWORD),
|
||||
)
|
||||
|
||||
################################################################################
|
||||
# OPNUMs and their corresponding structures
|
||||
################################################################################
|
||||
@@ -1169,6 +1247,8 @@ OPNUMS = {
|
||||
49 : (RCloseNotifyHandle, RCloseNotifyHandleResponse),
|
||||
51 : (RControlServiceExW, RControlServiceExWResponse),
|
||||
56 : (RQueryServiceConfigEx, RQueryServiceConfigExResponse),
|
||||
60 : (RCreateWowService, RCreateWowServiceResponse),
|
||||
64 : (ROpenSCManager2, ROpenSCManager2Response),
|
||||
}
|
||||
|
||||
################################################################################
|
||||
@@ -1289,6 +1369,30 @@ def hRCreateServiceW(dce, hSCManager, lpServiceName, lpDisplayName, dwDesiredAcc
|
||||
createService['dwPwSize'] = dwPwSize
|
||||
return dce.request(createService)
|
||||
|
||||
def hRCreateWowServiceW(dce, hSCManager, lpServiceName, lpDisplayName, dwDesiredAccess=SERVICE_ALL_ACCESS,
|
||||
dwServiceType=SERVICE_WIN32_OWN_PROCESS, dwStartType=SERVICE_AUTO_START, dwErrorControl=SERVICE_ERROR_IGNORE,
|
||||
lpBinaryPathName=NULL, lpLoadOrderGroup=NULL, lpdwTagId=NULL, lpDependencies=NULL, dwDependSize=0, lpServiceStartName=NULL,
|
||||
lpPassword=NULL, dwPwSize=0, dwServiceWowType: int=IMAGE_FILE_MACHINE_AMD64):
|
||||
createService = RCreateWowService()
|
||||
createService['hSCManager'] = hSCManager
|
||||
createService['lpServiceName'] = checkNullString(lpServiceName)
|
||||
createService['lpDisplayName'] = checkNullString(lpDisplayName)
|
||||
createService['dwDesiredAccess'] = dwDesiredAccess
|
||||
createService['dwServiceType'] = dwServiceType
|
||||
createService['dwStartType'] = dwStartType
|
||||
createService['dwErrorControl'] = dwErrorControl
|
||||
createService['lpBinaryPathName'] = checkNullString(lpBinaryPathName)
|
||||
createService['lpLoadOrderGroup'] = checkNullString(lpLoadOrderGroup)
|
||||
createService['lpdwTagId'] = lpdwTagId
|
||||
createService['lpDependencies'] = lpDependencies
|
||||
createService['dwDependSize'] = dwDependSize
|
||||
createService['lpServiceStartName'] = checkNullString(lpServiceStartName)
|
||||
createService['lpPassword'] = lpPassword
|
||||
createService['dwPwSize'] = dwPwSize
|
||||
createService['dwServiceWowType'] = dwServiceWowType
|
||||
return dce.request(createService)
|
||||
|
||||
|
||||
def hREnumDependentServicesW(dce, hService, dwServiceState, cbBufSize ):
|
||||
enumDependentServices = REnumDependentServicesW()
|
||||
enumDependentServices['hService'] = hService
|
||||
@@ -1361,6 +1465,12 @@ def hROpenSCManagerW(dce, lpMachineName='DUMMY\x00', lpDatabaseName='ServicesAct
|
||||
openSCManager['dwDesiredAccess'] = dwDesiredAccess
|
||||
return dce.request(openSCManager)
|
||||
|
||||
def hROpenSCManager2(dce, lpDatabaseName=NULL, dwDesiredAccess: int=SERVICE_START | SERVICE_STOP | SERVICE_CHANGE_CONFIG | SERVICE_QUERY_CONFIG | SERVICE_QUERY_STATUS | SERVICE_ENUMERATE_DEPENDENTS | SC_MANAGER_ENUMERATE_SERVICE):
|
||||
openSCManager = ROpenSCManager2()
|
||||
openSCManager['lpDatabaseName'] = checkNullString(lpDatabaseName)
|
||||
openSCManager['dwDesiredAccess'] = dwDesiredAccess
|
||||
return dce.request(openSCManager)
|
||||
|
||||
def hROpenServiceW(dce, hSCManager, lpServiceName, dwDesiredAccess= SERVICE_ALL_ACCESS):
|
||||
openService = ROpenServiceW()
|
||||
openService['hSCManager'] = hSCManager
|
||||
|
||||
@@ -0,0 +1,117 @@
|
||||
# Impacket - Collection of Python classes for working with network protocols.
|
||||
#
|
||||
# Copyright Fortra, LLC and its affiliated companies
|
||||
#
|
||||
# All rights reserved.
|
||||
#
|
||||
# This software is provided under a slightly modified version
|
||||
# of the Apache Software License. See the accompanying LICENSE file
|
||||
# for more information.
|
||||
#
|
||||
# Tested so far:
|
||||
# GetSupportedVersion
|
||||
# SetContext
|
||||
# StartShadowCopySet
|
||||
# AddToShadowCopySet
|
||||
# CommitShadowCopySet
|
||||
# ExposeShadowCopySet
|
||||
# RecoveryCompleteShadowCopySet
|
||||
# AbortShadowCopySet
|
||||
# IsPathSupported
|
||||
# IsPathShadowCopied
|
||||
# GetShareMapping
|
||||
# DeleteShareMapping
|
||||
# PrepareShadowCopySet
|
||||
#
|
||||
# Prerequisites and run commands: TESTING.md, "FSRVP: file-server shadow copies".
|
||||
# Set this to a regular SMB share on the configured target (not an administrative share).
|
||||
SHARE_NAME = "Windows"
|
||||
|
||||
from tests.dcerpc import DCERPCTests
|
||||
import pytest
|
||||
from impacket.dcerpc.v5 import fsrvp, rpcrt
|
||||
from impacket.uuid import string_to_bin
|
||||
import uuid
|
||||
import unittest
|
||||
|
||||
|
||||
class FSRVPTests(DCERPCTests):
|
||||
iface_uuid = fsrvp.MSRPC_UUID_FSRVP
|
||||
authn = True
|
||||
authn_level = rpcrt.RPC_C_AUTHN_LEVEL_PKT_PRIVACY
|
||||
string_binding = r"ncacn_np:{0.machine}[%s]" % fsrvp.MSRPC_NAMED_PIPE_FSRVP
|
||||
|
||||
def test_GetSupportedVersion(self):
|
||||
dce, transport = self.connect()
|
||||
self.addCleanup(dce.disconnect)
|
||||
request = fsrvp.GetSupportedVersion()
|
||||
resp = dce.request(request)
|
||||
resp.dump()
|
||||
self.assertEqual(resp['MinVersion'], 1)
|
||||
self.assertEqual(resp['MaxVersion'], 1)
|
||||
|
||||
def test_hGetSupportedVersion(self):
|
||||
dce, transport = self.connect()
|
||||
self.addCleanup(dce.disconnect)
|
||||
resp = fsrvp.hGetSupportedVersion(dce)
|
||||
resp.dump()
|
||||
self.assertEqual(resp['MinVersion'], 1)
|
||||
self.assertEqual(resp['MaxVersion'], 1)
|
||||
|
||||
def test_fsrvp(self):
|
||||
global SHARE_NAME
|
||||
# All test are passed in one function because almost all functions depend on each other and must be called sequentially.
|
||||
dce, transport = self.connect()
|
||||
# Shadow copy preparation
|
||||
# According to the documentation https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-fsrvp/e9f295cf-0f5c-4eec-8cfc-f2a031ae7805
|
||||
is_path_supported = fsrvp.hIsPathSupported(dce, r"\\%s\%s" % (self.machine, SHARE_NAME))
|
||||
self.assertEqual(is_path_supported["SupportedByThisProvider"], 1)
|
||||
machine_name = is_path_supported["OwnerMachineName"][:-1:]
|
||||
version_call = fsrvp.hGetSupportedVersion(dce) # Always MinVersion == MaxVersion == 1
|
||||
self.assertEqual(version_call["MinVersion"], 1)
|
||||
self.assertEqual(version_call["MaxVersion"], 1)
|
||||
fsrvp.hSetContext(dce, fsrvp.ContextValues.CTX_FILE_SHARE_BACKUP)
|
||||
client_set_id = string_to_bin(str(uuid.uuid4()))
|
||||
start_shadow_copy_set = fsrvp.hStartShadowCopySet(dce, client_set_id)
|
||||
shadow_copy_set_id = start_shadow_copy_set['pShadowCopySetId']
|
||||
client_shadow_copy_id = string_to_bin(str(uuid.uuid4()))
|
||||
add_to_shadow_copy = fsrvp.hAddToShadowCopySet(dce, client_shadow_copy_id, shadow_copy_set_id, f"\\\\{machine_name}\\{SHARE_NAME}")
|
||||
shadow_copy_id = add_to_shadow_copy['ShadowCopyId']
|
||||
prepare_shadow_copy = fsrvp.hPrepareShadowCopySet(dce, shadow_copy_set_id, 30000)
|
||||
self.assertEqual(prepare_shadow_copy['ErrorCode'], 0) # Server ready to create a shadow copy
|
||||
# Shadow copy creation
|
||||
fsrvp.hCommitShadowCopySet(dce, shadow_copy_set_id, 30000)
|
||||
fsrvp.hExposeShadowCopySet(dce, shadow_copy_set_id, 30000)
|
||||
share_mapping = fsrvp.hGetShareMapping(dce, shadow_copy_id, shadow_copy_set_id, f'\\\\{machine_name}\\{SHARE_NAME}')
|
||||
share_name = share_mapping['ShareMapping']['ShareMapping1']['ShadowCopyShareName'] # Server are exposed shadow copy of share to this share name
|
||||
fsrvp.hRecoveryCompleteShadowCopySet(dce, shadow_copy_set_id)
|
||||
# Check if path shadow copied:
|
||||
is_path_shadow_copied = fsrvp.hIsPathShadowCopied(dce, f'\\\\{machine_name}\\{SHARE_NAME}')
|
||||
self.assertEqual(is_path_shadow_copied["ShadowCopyPresent"], 1)
|
||||
# Shadow copy deletion
|
||||
delete_share_mapping = fsrvp.hDeleteShareMapping(dce, shadow_copy_set_id, shadow_copy_id, f'\\\\{machine_name}\\{SHARE_NAME}')
|
||||
|
||||
def test_abort_shadow_copy(self):
|
||||
global SHARE_NAME
|
||||
dce, transport = self.connect()
|
||||
is_path_supported = fsrvp.hIsPathSupported(dce, r"\\%s\%s" % (self.machine, SHARE_NAME))
|
||||
self.assertEqual(is_path_supported["SupportedByThisProvider"], 1)
|
||||
machine_name = is_path_supported["OwnerMachineName"][:-1:]
|
||||
version_call = fsrvp.hGetSupportedVersion(dce)
|
||||
self.assertEqual(version_call["MinVersion"], 1)
|
||||
self.assertEqual(version_call["MaxVersion"], 1)
|
||||
fsrvp.hSetContext(dce, fsrvp.ContextValues.CTX_FILE_SHARE_BACKUP)
|
||||
client_set_id = string_to_bin(str(uuid.uuid4()))
|
||||
start_shadow_copy_set = fsrvp.hStartShadowCopySet(dce, client_set_id)
|
||||
shadow_copy_set_id = start_shadow_copy_set['pShadowCopySetId']
|
||||
fsrvp.hAbortShadowCopySet(dce, shadow_copy_set_id)
|
||||
|
||||
|
||||
@pytest.mark.remote
|
||||
class FSRVPTestsSMBTransport(FSRVPTests, unittest.TestCase):
|
||||
transfer_syntax = DCERPCTests.TRANSFER_SYNTAX_NDR
|
||||
|
||||
|
||||
# Process command-line arguments.
|
||||
if __name__ == "__main__":
|
||||
unittest.main(verbosity=1)
|
||||
@@ -10,6 +10,8 @@
|
||||
#
|
||||
# Tested so far:
|
||||
# ROpenSCManagerW
|
||||
# ROpenSCManager2
|
||||
# RCreateWowService
|
||||
# RControlService
|
||||
# RDeleteService
|
||||
# RLockServiceDatabase
|
||||
@@ -45,13 +47,16 @@
|
||||
# RSetServiceStatus
|
||||
# RCreateServiceWOW64W
|
||||
#
|
||||
# Prerequisites for the new methods: TESTING.md, "SCMR: RCreateWowService and ROpenSCManager2".
|
||||
#
|
||||
import time
|
||||
import pytest
|
||||
import unittest
|
||||
from uuid import uuid4
|
||||
from struct import unpack
|
||||
from tests.dcerpc import DCERPCTests
|
||||
|
||||
from impacket.dcerpc.v5 import scmr
|
||||
from impacket.dcerpc.v5 import rpcrt, scmr
|
||||
from impacket.dcerpc.v5.ndr import NULL
|
||||
from impacket.crypto import encryptSecret
|
||||
from impacket.uuid import string_to_bin
|
||||
@@ -61,6 +66,91 @@ from impacket import ntlm
|
||||
class SCMRTests(DCERPCTests):
|
||||
iface_uuid = scmr.MSRPC_UUID_SCMR
|
||||
authn = True
|
||||
|
||||
def call_optional_method(self, method_name, call, *args, **kwargs):
|
||||
try:
|
||||
return call(*args, **kwargs)
|
||||
except rpcrt.DCERPCException as exc:
|
||||
# recv() currently reports RPC faults by name without a numeric code.
|
||||
unsupported_opnums = (0x1c010002, 0x16c9a001)
|
||||
if (exc.get_error_code() in unsupported_opnums or
|
||||
exc.error_string in ('nca_s_op_rng_error', 'rpc_s_op_rng_error') or
|
||||
(isinstance(exc, scmr.DCERPCSessionError) and
|
||||
exc.get_error_code() == 120)): # ERROR_CALL_NOT_IMPLEMENTED
|
||||
self.skipTest('%s is not supported by the target' % method_name)
|
||||
raise
|
||||
|
||||
def test_ROpenSCManager2(self):
|
||||
dce, rpc_transport = self.connect()
|
||||
self.addCleanup(dce.disconnect)
|
||||
request = scmr.ROpenSCManager2()
|
||||
request['lpDatabaseName'] = 'ServicesActive\x00'
|
||||
request['dwDesiredAccess'] = scmr.SC_MANAGER_CONNECT
|
||||
resp = self.call_optional_method('ROpenSCManager2', dce.request, request)
|
||||
self.addCleanup(scmr.hRCloseServiceHandle, dce, resp['lpScHandle'])
|
||||
resp.dump()
|
||||
self.assertEqual(resp['ReturnCode'], 0)
|
||||
|
||||
def test_hROpenSCManager2(self):
|
||||
dce, rpc_transport = self.connect()
|
||||
self.addCleanup(dce.disconnect)
|
||||
resp = self.call_optional_method('ROpenSCManager2', scmr.hROpenSCManager2,
|
||||
dce, 'ServicesActive', scmr.SC_MANAGER_CONNECT)
|
||||
self.addCleanup(scmr.hRCloseServiceHandle, dce, resp['lpScHandle'])
|
||||
resp.dump()
|
||||
self.assertEqual(resp['ReturnCode'], 0)
|
||||
|
||||
def create_wow_service_and_query(self, use_helper):
|
||||
dce, rpc_transport = self.connect()
|
||||
self.addCleanup(dce.disconnect)
|
||||
manager = scmr.hROpenSCManagerW(
|
||||
dce, dwDesiredAccess=scmr.SC_MANAGER_CREATE_SERVICE)['lpScHandle']
|
||||
self.addCleanup(scmr.hRCloseServiceHandle, dce, manager)
|
||||
service_name = 'ImpacketTest_' + uuid4().hex
|
||||
display_name = service_name
|
||||
# Only create a disabled record; no service binary is installed or run.
|
||||
binary_path = r'C:\impacket-test-service.exe'
|
||||
if use_helper:
|
||||
resp = self.call_optional_method('RCreateWowService', scmr.hRCreateWowServiceW,
|
||||
dce, manager, service_name, display_name,
|
||||
dwStartType=scmr.SERVICE_DISABLED,
|
||||
lpBinaryPathName=binary_path,
|
||||
dwServiceWowType=scmr.IMAGE_FILE_MACHINE_I386)
|
||||
else:
|
||||
request = scmr.RCreateWowService()
|
||||
request['hSCManager'] = manager
|
||||
request['lpServiceName'] = service_name + '\x00'
|
||||
request['lpDisplayName'] = display_name + '\x00'
|
||||
request['dwDesiredAccess'] = scmr.SERVICE_ALL_ACCESS
|
||||
request['dwServiceType'] = scmr.SERVICE_WIN32_OWN_PROCESS
|
||||
request['dwStartType'] = scmr.SERVICE_DISABLED
|
||||
request['dwErrorControl'] = scmr.SERVICE_ERROR_IGNORE
|
||||
request['lpBinaryPathName'] = binary_path + '\x00'
|
||||
request['lpLoadOrderGroup'] = NULL
|
||||
request['lpdwTagId'] = NULL
|
||||
request['lpDependencies'] = NULL
|
||||
request['dwDependSize'] = 0
|
||||
request['lpServiceStartName'] = NULL
|
||||
request['lpPassword'] = NULL
|
||||
request['dwPwSize'] = 0
|
||||
request['dwServiceWowType'] = scmr.IMAGE_FILE_MACHINE_I386
|
||||
resp = self.call_optional_method('RCreateWowService', dce.request, request)
|
||||
service = resp['lpServiceHandle']
|
||||
self.addCleanup(scmr.hRCloseServiceHandle, dce, service)
|
||||
self.addCleanup(scmr.hRDeleteService, dce, service)
|
||||
resp.dump()
|
||||
self.assertEqual(resp['ErrorCode'], 0)
|
||||
config = scmr.hRQueryServiceConfigW(dce, service)['lpServiceConfig']
|
||||
self.assertEqual(config['lpDisplayName'], display_name + '\x00')
|
||||
self.assertEqual(config['lpBinaryPathName'], binary_path + '\x00')
|
||||
self.assertEqual(config['dwServiceType'], scmr.SERVICE_WIN32_OWN_PROCESS)
|
||||
self.assertEqual(config['dwStartType'], scmr.SERVICE_DISABLED)
|
||||
|
||||
def test_RCreateWowService(self):
|
||||
self.create_wow_service_and_query(use_helper=False)
|
||||
|
||||
def test_hRCreateWowServiceW(self):
|
||||
self.create_wow_service_and_query(use_helper=True)
|
||||
|
||||
def get_service_handle(self, dce):
|
||||
lpMachineName = 'DUMMY\x00'
|
||||
|
||||
Reference in new issue
Block a user