Using the method described in the MSDN for registering a Windows Service (ms-help://MS.VSCC.v80/MS.MSDN.v80/MS.WIN32COM.v10.en/dllproc/base/createservice.htm) and using similar code to the supplied example:
schService = CreateService(
schSCManager, // SCManager database
TEXT("Sample_Srv"), // name of service
lpszDisplayName, // service name to display
SERVICE_ALL_ACCESS, // desired access
SERVICE_WIN32_OWN_PROCESS, // service type
SERVICE_DEMAND_START, // start type
SERVICE_ERROR_NORMAL, // error control type
szPath, // path to service's binary
NULL, // no load ordering group
NULL, // no tag identifier
NULL, // no dependencies
NULL, // LocalSystem account
NULL); // no password
My issue is that, although the service is registered and works perfectly, in msconfig.msc the service has 'Take No Action' in the recovery options. Is there a way I can programatically change this so that upon failure it restarts?
Take a look at ChangeServiceConfig2 for setting those types of service options.
You might be able to set this using the sc command.
sc failure "servicename" reset= 0 actions= restart/30000////
This will tell it to reset the failure counter after 0 days (never), and restart after 30 seconds on the first failure with no action for the second and later failures.
Performed further digging in the MSDN - it wasn't particularly easy to find but it appears
ChangeServiceConfig2 (http://msdn.microsoft.com/en-us/library/ms681988(VS.85).aspx)
BOOL WINAPI ChangeServiceConfig2(
__in SC_HANDLE hService,
__in DWORD dwInfoLevel,
__in_opt LPVOID lpInfo
);
When param dwInfoLevel is SERVICE_CONFIG_FAILURE_ACTIONS (2) then the lpInfo parameter is a pointer to a SERVICE_FAILURE_ACTIONS structure.
SERVICE_FAILURE_ACTIONS Structure
http://msdn.microsoft.com/en-us/library/ms685939(VS.85).aspx
Where you can configure the 'optional' service settings as you wish.
Related
So I'm making a crackme and one of the parts is to hook a certain function and wait for a certain combination a params to happen, then the challenge is done.
For that, I'm creating a driver to inject a DLL into processes that have a specific DLL and hook a certain function.
I'm doing it by
Getting a handle for the DLL to inject
ZwCreateFile(
&DeviceExtension->HookDllHandle,
GENERIC_ALL,
&Attributes,
&StatusBlock,
NULL,
0,
0,
FILE_OPEN,
FILE_SYNCHRONOUS_IO_NONALERT,
NULL,
0
)
Then, registering a LoadImageNotifyRoutine inside driver main
PsSetLoadImageNotifyRoutine(ImageCBK);
What's supposed to happen:
I check the if the needed DLL (that will export my function) is loaded.
By being inside the context of the process that invoked the callback, I create a section with ZwCreateSection, then map the dll into that section and call the DLL's entry point by creating a new thread.
After that, the hooking should be no problem.
Even though the IRQL for ZwCreateSection and ZwMapViewOfSection allows their use inside a notify routine, still ZwMapViewOfSection hangs every time I try to use it.
I've been using some code from Beholder
status = ObOpenObjectByPointer(PsGetCurrentProcess(), OBJ_KERNEL_HANDLE, NULL, STANDARD_RIGHTS_ALL, NULL, KernelMode, &ProcessHandle);
if (!NT_SUCCESS(status))
{
DbgPrint("Unable to get process handle\n");
return STATUS_SEVERITY_ERROR;
}
// Create a new section for DLL mapping
InitializeObjectAttributes(&Attributes, NULL, OBJ_KERNEL_HANDLE, NULL, NULL);
status = ZwCreateSection(&DllSectionHandle, SECTION_MAP_WRITE | SECTION_MAP_READ | SECTION_MAP_EXECUTE | SECTION_QUERY, &Attributes, NULL, PAGE_EXECUTE_READ, SEC_IMAGE, DeviceExtension->HookDllHandle);
if (!NT_SUCCESS(status))
{
ZwClose(ProcessHandle);
DbgPrint("Section creation failed %08X\n", status);
return status;
}
DbgPrint("Section created %08X\n", DllSectionHandle);
// Map DLL on the section
status = ZwMapViewOfSection(DllSectionHandle, ProcessHandle, &DllBaseAddress, 0, 0, NULL, &DllViewSize, ViewUnmap, 0, PAGE_EXECUTE_READ);
if (!NT_SUCCESS(status))
{
ZwClose(ProcessHandle);
ZwClose(DllSectionHandle);
DbgPrint("Unable to map section %08X\n", status);
return status;
}
DbgPrint("Mapped DLL: %08X\n", DllBaseAddress);
Sadly, it never shows the last DbgPrint with the DllBaseAddress
simply read documentation
The operating system calls the driver's load-image notify routine at
PASSIVE_LEVEL inside a critical region with normal kernel APCs always
disabled
and
To avoid deadlocks, load-image notify routines must not call system
routines that map, allocate, query, free, or perform other operations
on user-space virtual memory.
you ignore this and call routine ZwMapViewOfSection that map. and got deadlock
solution is simply and elegant - insert normal kernel mode APC to current thread inside ImageCBK. because this APC is disabled here - it executed already after you return from ImageCBK -just system exit from critical region and enable APC. at this point your apc KernelRoutine/NormalRoutine will be called. and exactly inside NormalRoutine you must map
as shown in title, i search on google for this question, but there seems that no way get serial number via WPD(Windows Portable Device) api, and in MSDN, i found the WPD_DEVICE_SERIAL_NUMBER property of Portable Device, can anyone tell me how to get this property using wpd api?
The C++ sample can be found here and here
Bit of a process. Basic steps are as follows:
Get and populate a IPortableDeviceValues of your client info
// Create our client information collection
ThrowIfFailed(CoCreateInstance(
CLSID_PortableDeviceValues,
nullptr,
CLSCTX_INPROC_SERVER,
IID_PPV_ARGS(&clientInfo)));
// We have to provide at the least our name, version, revision
ThrowIfFailed(clientInfo->SetStringValue(
WPD_CLIENT_NAME,
L"My super cool WPD client"));
ThrowIfFailed(clientInfo->SetUnsignedIntegerValue(
WPD_CLIENT_MAJOR_VERSION,
1));
ThrowIfFailed(clientInfo->SetUnsignedIntegerValue(
WPD_CLIENT_MINOR_VERSION,
0));
ThrowIfFailed(clientInfo->SetUnsignedIntegerValue(
WPD_CLIENT_REVISION,
1));
Get an IPortableDevice with CoCreateInstance
// A WPD device is represented by an IPortableDevice instance
ThrowIfFailed(CoCreateInstance(
CLSID_PortableDevice,
nullptr,
CLSCTX_INPROC_SERVER,
IID_PPV_ARGS(&device)));
Connect to the device using IPortableDevice::Open, passing the device's ID and the above client info
device->Open(deviceId.c_str(), clientInfo);
Get the device's IPortableDeviceContent using IPortableDevice::Content
CComPtr<IPortableDeviceContent> retVal;
ThrowIfFailedWithMessage(
device.Content(&retVal),
L"! Failed to get IPortableDeviceContent from IPortableDevice");
Get the content's IPortableDeviceProperties using IPortableDeviceContent::Properties
CComPtr<IPortableDeviceProperties> retVal;
ThrowIfFailedWithMessage(
content.Properties(&retVal),
L"! Failed to get IPortableDeviceProperties from IPortableDeviceContent");
Get the properties' IPortableDeviceValues using IPortableDeviceProperties::GetValues, passing "DEVICE" for pszObjectID and nullptr for pKeys
CComPtr<IPortableDeviceValues> retVal;
ThrowIfFailedWithMessage(
properties.GetValues(objectId.c_str(), nullptr, &retVal),
L"! Failed to get IPortableDeviceValues from IPortableDeviceProperties");
Get the serial number from the values using IPortableDeviceValues::GetStringValue, passing WPD_DEVICE_SERIAL_NUMBER for key
propertyKey = WPD_DEVICE_SERIAL_NUMBER;
LPWSTR value = nullptr;
ThrowIfFailedWithMessage(
values.GetStringValue(propertyKey, &value),
L"! Failed to get string value from IPortableDeviceValues");
propertyValue = value;
if (value != nullptr)
{
CoTaskMemFree(value);
}
By no means a complete listing, sorry. The ThrowIf* functions are just basic helpers I wrote to go from checking HRESULTs to throwing exceptions. Hopefully this points you in the right direction.
Additional references:
The dimeby8 blog
WPD Application Programming Interface
I'm having a problem with ChangeServiceConfig2(...SERVICE_CONFIG_TRIGGER_INFO...)
Relevant code:
WCHAR test[] = L"TEST12";
SERVICE_TRIGGER_SPECIFIC_DATA_ITEM stdata {
SERVICE_TRIGGER_DATA_TYPE_STRING,
wcslen(test)*sizeof(WCHAR),
reinterpret_cast<BYTE*>(test)
};
SERVICE_TRIGGER st {
SERVICE_TRIGGER_TYPE_NETWORK_ENDPOINT,
SERVICE_TRIGGER_ACTION_SERVICE_START,
const_cast<GUID*>(&NAMED_PIPE_EVENT_GUID),
1, &stdata
};
ChangeServiceConfig2(Service, SERVICE_CONFIG_TRIGGER_INFO, &st);
This causes an Access Violation on address 00000009, so clearly an unchecked null pointer. And it's not a null pointer in st or stdata. The address 00000009 does not depend on the length of test[].
Stack dump:
rpcrt4.dll!NdrpEmbeddedRepeatPointerBufferSize()
rpcrt4.dll!NdrConformantArrayBufferSize()
rpcrt4.dll!NdrSimpleStructBufferSize()
rpcrt4.dll!NdrpUnionBufferSize()
rpcrt4.dll!_NdrNonEncapsulatedUnionBufferSize#12()
rpcrt4.dll!NdrComplexStructBufferSize()
rpcrt4.dll!NdrClientCall2() rpcrt4.dll!_NdrClientCall4()
sechost.dll!ChangeServiceConfig2W()
The Service member is not the problem, or ChangeServiceConfig2 itself: I can set the service description via ChangeServiceConfig2(Service, SERVICE_CONFIG_DESCRIPTION, &desc);. The problem appears to be in the parsing of SERVICE_TRIGGER. Named Pipe service triggers apparently work for the Remote Registry service, so it's not fundamentally broken.
Q: which part of my SERVICE_TRIGGER is wrong?
Obviously there is at least one bug in Windows; at the very least it fails in parameter validation.
The SERVICE_TRIGGER object is correct, but ChangeServiceConfig2 wants a SERVICE_TRIGGER_INFO. Simple solution: wrap st using SERVICE_TRIGGER_INFO sti{ 1, &st, NULL };
I'm trying to implement rebooting of a remote computer with InitiateShutdown API using the following code, but it fails with RPC_S_SERVER_UNAVAILABLE or 1722 error code:
//Process is running as administrator
//Select a remote machine to reboot:
//INFO: Tried it with and w/o two opening slashes.
LPCTSTR pServerName = L"192.168.42.105";
//Or use 127.0.0.1 if you don't have access to another machine on your network.
//This will attempt to reboot your local machine.
//In that case make sure to call shutdown /a /m \\127.0.0.1 to cancel it.
if(AdjustPrivilege(NULL, L"SeShutdownPrivilege", TRUE) &&
AdjustPrivilege(pServerName, L"SeRemoteShutdownPrivilege", TRUE))
{
int nErrorCode = ::InitiateShutdown(pServerName, NULL, 30,
SHUTDOWN_INSTALL_UPDATES | SHUTDOWN_RESTART, 0);
//Receive nErrorCode == 1722, or RPC_S_SERVER_UNAVAILABLE
}
BOOL AdjustPrivilege(LPCTSTR pStrMachine, LPCTSTR pPrivilegeName, BOOL bEnable)
{
HANDLE hToken;
TOKEN_PRIVILEGES tkp;
BOOL bRes = FALSE;
if(!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))
return FALSE;
if(LookupPrivilegeValue(pStrMachine, pPrivilegeName, &tkp.Privileges[0].Luid))
{
tkp.PrivilegeCount = 1;
tkp.Privileges[0].Attributes = bEnable ? SE_PRIVILEGE_ENABLED : SE_PRIVILEGE_REMOVED;
bRes = AdjustTokenPrivileges(hToken, FALSE, &tkp, 0, (PTOKEN_PRIVILEGES)NULL, 0);
int nOSError = GetLastError();
if(bRes)
{
if(nOSError != ERROR_SUCCESS)
bRes = FALSE;
}
}
CloseHandle(hToken);
return bRes;
}
So to prepare for this code to run I do the following on this computer, which is Windows 7 Pro (as I would do for the Microsoft's shutdown tool):
Run the following "as administrator" to allow SMB access to the logged in user D1 on the 192.168.42.105 computer (per this answer):
NET USE \\192.168.42.105\IPC$ 1234 /USER:D1
Run the process with my code above "as administrator".
And then do the following on remote computer, or 192.168.42.105, that has Windows 7 Pro (per answer here with most upvotes):
Control Panel, Network and Sharing Center, Change Advanced Sharing settings
"Private" enable "Turn on File and Printer sharing"
Set the following key:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System
LocalAccountTokenFilterPolicy=dword:1
RUN secpol.msc, then go to Local Security Policy, Security Settings, Local Policies, User Rights Assignment. Add "Everyone" to "Force shutdown from a remote system". (Just remember to remove it after you're done testing!)
Note that the following shutdown command seems to work just fine to reboot the remote computer:
shutdown /r /m \\192.168.42.105 /t 30
What am I missing with my code?
EDIT:
OK. I will admit that I was merely interested in why InitiateShutdown doesn't seem to "want" to work with a remote server connection, while InitiateSystemShutdownEx or InitiateSystemShutdown had no issues at all. (Unfortunately the latter two did not have the dwShutdownFlags parameter, which I needed to pass the SHUTDOWN_INSTALL_UPDATES flag to, which caused my persistence...)
At this point I had no other way of finding out than dusting out a copy of WinDbg... I'm still trying to dig into it, but so far this is what I found...
(A) It turns out that InitiateSystemShutdownEx internally uses a totally different RPC call. W/o too many details, it initiates RPC binding with RpcStringBindingComposeW using the following parameters:
ObjUuid = NULL
ProtSeq = ncacn_np
NetworkAddr = \\192.168.42.105
EndPoint = \\PIPE\\InitShutdown
Options = NULL
or the following binding string:
ncacn_np:\\\\192.168.42.105[\\PIPE\\InitShutdown]
(B) While InitiateShutdown on the other hand uses the following binding parameters:
ObjUuid = 765294ba-60bc-48b8-92e9-89fd77769d91
ProtSeq = ncacn_ip_tcp
NetworkAddr = 192.168.42.105
EndPoint = NULL
Options = NULL
which it later translates into the following binding string:
ncacn_np:\\\\192.168.42.105[\\PIPE\\lsarpc]
that it uses to obtain the RPC handle that it passes to WsdrInitiateShutdown (that seems to have its own specification):
So as you see, the InitiateShutdown call is technically treated as Unknown RPC service (for the UUID {765294ba-60bc-48b8-92e9-89fd77769d91}), which later causes a whole bunch of credential checks between the server and the client:
which, honestly, I'm not sure I want to step into with a low-level debugger :)
At this stage I will say that I am not very well versed on "Local Security Authority" interface (or the \PIPE\lsarpc named pipe configuration.) So if anyone knows what configuration is missing on the server side to allow this RPC call to go through, I would appreciate if you could post your take on it?
My Windows service creates 2 Events with CreateEvent for communication with a user app.
The service and the user app are not running under the same user account.
The user app opens the event and set it to signaled without error. But the event is never received by the service. The other event works in the opposite direction.
So I think the events miss the syncronization right.
Service:
SECURITY_ATTRIBUTES security;
ZeroMemory(&security, sizeof(security));
security.nLength = sizeof(security);
ConvertStringSecurityDescriptorToSecurityDescriptor(L"D:P(A;OICI;GA;;;SY)(A;OICI;GA;;;BA)(A;OICI;GWGR;;;IU)", SDDL_REVISION_1, &security.lpSecurityDescriptor, NULL);
EvtCreateNewUserSession = CreateEventW(
&security, // security attributes
TRUE, // manual-reset event
FALSE, // initial state is not signaled
L"Global\\MyEvent" // object name
);
Interactive App:
HANDLE EvtCreateNewUserSession = OpenEventW(
EVENT_MODIFY_STATE | SYNCHRONIZE, // default security attributes
FALSE, // initial state is not signaled
L"Global\\MyEvent" // object name
;
Thanks for your help,
Olivier
Instead of using 'string SDDL rights' (like GA) use 0xXXXXXXXX format (you can combine flags and then convert them to hex-string).
For example this SDDL: D:(A;;0x001F0003;;;BA)(A;;0x00100002;;;AU) creates DACL for:
- BA=Administrators, 0x001F0003=EVENT_ALL_ACCESS (LocalSystem and LocalService are in Administrators group, but NetworkService is not)
- AU=Authenticated Users, 0x00100002=SYNCHRONIZE | EVENT_MODIFY_STATE
http://msdn.microsoft.com/en-us/library/windows/desktop/aa374928(v=vs.85).aspx - field rights
A string that indicates the access rights controlled by the ACE.
This string can be a hexadecimal string representation of the access rights,
such as "0x7800003F", or it can be a concatenation of the following strings.
...