How to detour LdrLoadDll correctly - detours

Here is the code I'm using to detour the LdrLoadDll.
NTSTATUS(NTAPI* Real_LdrLoadDll)(PWCHAR, ULONG, PUNICODE_STRING, PHANDLE);
typedef NTSTATUS(NTAPI* pLdrLoadDll)(PWCHAR, ULONG, PUNICODE_STRING, PHANDLE);
NTSTATUS NTAPI Mine_LdrLoadDll(PWCHAR a0, ULONG a1, PUNICODE_STRING a2, PHANDLE a3)
{
wprintf(L"[+] Mine_LdrLoadDll called on %s\n", a2->Buffer);
return Real_LdrLoadDll(a0, a1, a2, a3);
}
BOOL APIENTRY DllMain(HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
{
wprintf(L"[+] DLL_PROCESS_ATTACH called in DllFirstEntry\n");
DetourTransactionBegin();
DetourUpdateThread(GetCurrentThread());
Real_LdrLoadDll = reinterpret_cast<pLdrLoadDll>(GetProcAddress(GetModuleHandleA("ntdll.dll"), "LdrLoadDll"));
LONG l = DetourAttach(&(PVOID&)Real_LdrLoadDll, Mine_LdrLoadDll);
if (l != 0)
{
wprintf(L"DetourAttach failed");
}
PVOID* ppbFailedPointer = nullptr;
LONG error = DetourTransactionCommitEx(&ppbFailedPointer);
if (error != 0)
{
__debugbreak();
}
break;
}
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
}
I've set a breakpoint on return Real_LdrLoadDll(a0, a1, a2, a3), and get there. So I know the detour worked. But on executing Real_LdrLoadDll I get an unhandled exception.
Unhandled exception at 0x00007FFD95D016B7 (ntdll.dll) in BlockDll.exe: 0xC0000005: Access violation reading location 0x00000000B69CE680.
Is this the correct way or am I missing something?
From other posts related to this type of topic (Detour on a function), I see lots of questions about why are you doing this, can't you do this another way. Or just flat out 'Don't do this'.
The goal is to experiment with blocking dll injections into an application based on a blacklist of known problem dlls. I'm basing the possibility of this working on the WindowsDllBlocklist.cpp in Mozilla.
Maybe this won't work for all cases and maybe it is problematic. Maybe it is just a plain bad idea and we should all live with other developers injecting their dlls into our applications and causing problems.
But the short answer is that it looks like someone else is doing this and it works. So I would like to learn how to do it and see if we can help to make the application more stable.
Thanks
Mark

Related

How to test if a critical section is locked, without entering it? Or, how to wait until a critsec is owned by another thread?

I am working on forcing certain deadlock scenarios to reproduce consistently, for dev purposes. In doing so it would be helpful to be able for a thread to wait until a critical section is locked by another thread, then forcing it to block.
So, I want something like:
void TryToWaitForBlock(CriticalSection& cs, DWORD ms)
{
// wait until this CS is blocked, then return
}
...
void someFunction()
{
// ...
TryToWaitForBlock(cs, 5000);// this will give much more time for the crit sec to block by other threads, increasing the chance that the next call will block.
EnterCriticalSection(cs);// normally this /very/ rarely blocks. When it does, it deadlocks.
// ...
}
TryEnterCriticalSection would be perfect, but because it will actually enter the critical section, it is not usable. Is there a similar function that will do the test, but NOT also try to enter it?
bool TryToWaitForBlock( CRITICAL_SECTION& cs, DWORD ms )
{
LARGE_INTEGER freq;
QueryPerformanceFrequency( &freq );
LARGE_INTEGER now;
QueryPerformanceCounter( &now );
LARGE_INTEGER waitTill;
waitTill.QuadPart = static_cast<LONGLONG>(now.QuadPart + freq.QuadPart * (ms / 1000.0));
while( now.QuadPart < waitTill.QuadPart ) {
if( NULL != static_cast<volatile HANDLE&>(cs.OwningThread) ) {
return true;
}
QueryPerformanceCounter( &now );
}
return false;
}

Why won't my registry read work vc++?

I am using the following code:
bool DllGuard()
{
HKEY keyHandle;
bool rgValue = bool();
DWORD Size;
DWORD Type;
try
{
if (RegOpenKeyEx(HKEY_CURRENT_USER, "Software\\MSB", 0, KEY_QUERY_VALUE, &keyHandle) == ERROR_SUCCESS)
{
Type = REG_DWORD;
Size = sizeof(DWORD);
RegQueryValueEx(keyHandle, "DllGuard", NULL, &Type, (LPBYTE)rgValue,&Size);
}
RegCloseKey(keyHandle);
if (rgValue == false)
{
return true;
}
else
{
return false;
}
}
catch (...)
{
return false;
}
}
BOOL APIENTRY DllMain(HINSTANCE hInstance, DWORD fdwReason, LPVOID lpReserved)
{
if(fdwReason == DLL_PROCESS_ATTACH)
{
if (DllGuard())
{
DisableThreadLibraryCalls(hInstance);
CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)&Main, NULL, 0, NULL);
}
else
{
exit(0);
}
}
return TRUE;
}
First time around it works fine, thus creating the thread in DllMain.
The second time around, it does not work as it still creates the thread in DllMain even if the registry key is set to true.
Help!
Thanks!
(LPBYTE)rgValue shoud be (LPBYTE)&rgValue, as you want to pass a pointer (so rgValue is modified by RegQueryValueEx. And it should be DWORD rgValue instead of bool (maybe you know that they happen to be of the same size, but it's harder to read).
Also it would be great to check the return value of RegQueryValueEx, so we have a chance to know what's wrong next time when it fails. (If you have no idea how to handle a failure, maybe write something with OutputDebugString, to be seen in sysinternals' dbgview.exe).
I can't comment but I think is important to know that you should consider not using DLLMain at all unless you know what you are doing... Give a look at the MSDN pages for DllMain entry point:
Warning There are serious limits on what you can do in a DLL entry point. To provide more complex initialization, create an initialization routine for the DLL. You can require applications to call the initialization routine before calling any other routines in the DLL.
Or you can Be afraid. Be very afraid

CLR 2.0 memory leak: Failing to release COM object on unload

I have come across a bug in CLR 2.0 that is resolved in CLR 4.0. It occurs when passing arrays across .NET COM interop and a COM exception is generated (E_FAIL). The details of how to reproduce this bug are below.
My problem is that it will be very difficult to force our clients to upgrade to .NET 4.0, so I'd like to implement a workaround. I can do so by calling obj->Release if I know that the bug has occured, but clearly this is dangerous if there's any chance of a false positive.
And so the question: What is the specification of this bug, and is it possible for me to identify it precisely?
I found .NET release notes for 4.0.1, 4.0.2, and 4.0.3, but the bug is not mentioned. There must be a significant changelist in the CLR transition from 2.0 to 4.0, and I guess this is not publicly available?
Obviously the code below makes little sense on its own, but it's the simplest reproduction of the issue that I could distill based on quite a large, complicated solution.
Thanks in advance for taking a look,
R
Important Edit
Unfortunately, I came back to try and investigate a little further, and it's possible that the code below does not actually reproduce the bug, which would be disappointing. However, in the actual application, the memory leak is clear. If someone is interested and I have time, I'll try to produce a valid example.
Code Overview
I have a .NET application, ConsoleApp.exe, here reproduced in C# although the original is F#. ConsoleApp.exe calls a managed assembly, managed.AComObject.dll, that exposes a COM object, AComObject. AComObject.get_TheObject() returns a VARIANT* pointing to a smart pointer, ASmartPtr, which allows me to override the AddRef and Release methods to observe the references held against the object.
When running ConsoleApp.exe with unmanaged code debugging enabled, I can see the reference counts on the SmartPtr. I change the CLR by adjusting the supportedRuntime property in ConsoleApp.exe.config with the following results:
v4.0 shows "DEBUGMSG::ASmartPtr::Release:0", at which point the SmartPtr is deleted.
v2.0.50727 shows "DEBUGMSG::ASmartPtr::Release:1" before it exits, a leak.
I include the bits of code I believe are relevant, but please shout if more is required; COM needs a lot of boilerplate code...!
ConsoleApp.exe
using managed.AComObject;
using System;
public static class Program
{
public static void Main()
{
AComObject an_obj = new AComObject();
object[] pData = new object[] { 1 };
object a_val = an_obj.get_TheObject(0, pData);
object[] pData2 = new object[] { a_val };
try
{
object obj3 = an_obj.get_TheObject(1, pData2);
}
catch (System.Exception)
{
// Makes no diff whether it's caught - still does not clean
}
}
}
AComObject.dll
AComObject.idl
interface IAComObject : IDispatch
{
[propget, id(1), helpstring("")] HRESULT DllName([out, retval] BSTR* pName);
[propget, id(2), helpstring("")] HRESULT TheObject([in] LONG count, [in, size_is(count)] VARIANT* pData, [out, retval] VARIANT* pObject);
};
[...]
library AComObjectLib
{
importlib("stdole2.tlb");
// Class information
[...]
coclass AComObject
{
[default] interface IAComObject;
};
};
AComObject.h
[...]
class ATL_NO_VTABLE CAComObject :
public CComObjectRootEx<CComSingleThreadModel>,
public CComCoClass<CAComObject, &CLSID_AComObject>,
public IDispatchImpl<IAComObject, &IID_IAComObject, &LIBID_AComObjectLib, /*wMajor =*/ 1, /*wMinor =*/ 0>
{
public:
DECLARE_REGISTRY_RESOURCEID(IDR_ACOMOBJECT)
BEGIN_COM_MAP(CAComObject)
COM_INTERFACE_ENTRY2(IDispatch, IAComObject)
COM_INTERFACE_ENTRY(IAComObject)
END_COM_MAP()
public:
CAComObject();
virtual /* [helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_DllName(
/* [retval][out] */ BSTR* pName);
virtual /* [helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_TheObject(
/* [in] */ LONG count,
/* [in, size_is(count)] */ VARIANT* pData,
/* [retval][out] */ VARIANT* pObject);
};
OBJECT_ENTRY_AUTO(CLSID_AComObject, CAComObject)
AComObject.cpp
class ASmartPtr : public IUnknown
{
int m_RC;
void DebugMsg(std::string msg)
{
std::stringstream _msg;
_msg << ".\nDEBUGMSG::ASmartPtr::" << msg << "\n";
OutputDebugStringA(_msg.str().c_str());
}
public:
ASmartPtr()
: m_RC(1)
{
DebugMsg(std::string("Created"));
}
virtual ULONG STDMETHODCALLTYPE AddRef()
{
ULONG refcnt = ++m_RC;
std::stringstream msg;
msg << "AddRef:" << refcnt;
DebugMsg(msg.str());
return refcnt;
}
virtual ULONG STDMETHODCALLTYPE Release()
{
ULONG refcnt = --m_RC;
std::stringstream msg;
msg << "Release:" << refcnt;
DebugMsg(msg.str());
if (m_RC == 0)
delete this;
return refcnt;
}
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, void** ppvObj)
{
if (!ppvObj) return E_POINTER;
if (iid == IID_IUnknown)
{
*ppvObj = this;
AddRef();
return NOERROR;
}
return E_NOINTERFACE;
}
};
[...]
STDMETHODIMP CAComObject::get_TheObject(LONG count, VARIANT* pData, VARIANT* pObject)
{
if (count == 1)
return E_FAIL;
CComVariant res;
res.punkVal = new ASmartPtr();
res.vt = VT_UNKNOWN;
res.Detach(pObject);
return S_OK;
}
managed.AComObject.dll
This is assembled from the COM object with the following post-build events to enable passing of arrays to get_TheObject() rather than references.
Batch File
call "C:\Program Files (x86)\Microsoft Visual Studio 9.0\Common7\Tools\vsvars32.bat"
echo "f" | xcopy /L/D/Y ..\Debug\AComObject.dll managed.AComObject.dll | find "AComObject" > nul
if not errorlevel 1 (
tlbimp ..\Debug\AComObject.dll /primary /keyfile:..\piakey.snk /out:managed.AComObject.dll
ildasm managed.AComObject.dll /out:managed.AComObject.raw.il
perl -p oneliner.pl < managed.AComObject.raw.il > managed.AComObject.il
ilasm managed.AComObject.il /dll /key=..\piakey.snk
)
set errorlevel=0
exit 0
oneliner.pl
$a = 1 if (/TheObject\(/);if ($a){s/object&/object\[\]/; s/marshal\( struct\) pData/marshal\( \[\]\) pData/; $a++; $a&=3;}
This simply changes the IL:
[in] object& marshal( struct) pData) runtime managed internalcall
to
[in] object[] marshal( []) pData) runtime managed internalcall
Some additional information
In considering my response to Hans's comment, I realised some relevant information is missing.
If no exception is thrown (i.e. E_FAIL is changed to S_OK), there is no leak. In the S_OK case, we can see the object reference count returning to 1 as we cross the .NET COM interop back into ConsoleApp.exe. In the E_FAIL case, the refcount remains at 2. In both cases, we can observe the finalizer reducing the refcount again as the application terminates (and observe the object destructor in the S_OK case), but in the E_FAIL case, this still leaves the refcount at 1 so the object is leaked. In CLR 4.0, all behaves as expected (i.e. refcount returns to 1 on passing back to ConsoleApp.exe even in the E_FAIL case).
We are considering upgrading to CLR 4.0 to resolve this leak, but it is not entirely trivial since it treats COM wrapped managed DLLs in a different way and this is a breaking change for some of our clients. If there was a way for me to precisely identify this bug, we could avoid the upgrade pain for a little longer.
In the end, the solution was rather simple, and we were able to proceed without upgrading. It was the old trick of adding a supportedRuntime and the additional attribute to the application.exe.config:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup useLegacyV2RuntimeActivationPolicy="true">
<supportedRuntime version="v4.0" />
</startup>
</configuration>
Without the attribute, the .NET2 code loads side-by-side into CLR2, and so we suffer the leak. The attribute allows the .NET2 code to be loaded directly into the CLR4, hence avoiding the leak. There is a detailed review of that attribute here: http://www.marklio.com/marklio/PermaLink,guid,ecc34c3c-be44-4422-86b7-900900e451f9.aspx.
This unfortunately leaves the memory leak extant for anyone using an application with such a config, but this is adequate for the time being.

D-Bus tutorial in C to communicate with wpa_supplicant

I'm trying to write some code to communicate with wpa_supplicant using DBUS. As I'm working in an embedded system (ARM), I'd like to avoid the use of Python or the GLib. I'm wondering if I'm stupid because I really have the feeling that there is no nice and clear documentation about D-Bus. Even with the official one, I either find the documentation too high level, or the examples shown are using Glib! Documentation I've looked at: http://www.freedesktop.org/wiki/Software/dbus
I found a nice article about using D-Bus in C: http://www.matthew.ath.cx/articles/dbus
However, this article is pretty old and not complete enough! I also found the c++-dbus API but also here, I don't find ANY documentation! I've been digging into wpa_supplicant and NetworkManager source code but it's quite a nightmare! I've been looking into the "low-level D-Bus API" as well but this doesn't tell me how to extract a string parameter from a D-Bus message! http://dbus.freedesktop.org/doc/api/html/index.html
Here is some code I wrote to test a little but I really have trouble to extract string values. Sorry for the long source code but if someone want to try it ... My D-Bus configuration seems fine because it "already" catches "StateChanged" signals from wpa_supplicant but cannot print the state:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <string.h>
#include <dbus/dbus.h>
//#include "wpa_supp_dbus.h"
/* Content of wpa_supp_dbus.h */
#define WPAS_DBUS_SERVICE "fi.epitest.hostap.WPASupplicant"
#define WPAS_DBUS_PATH "/fi/epitest/hostap/WPASupplicant"
#define WPAS_DBUS_INTERFACE "fi.epitest.hostap.WPASupplicant"
#define WPAS_DBUS_PATH_INTERFACES WPAS_DBUS_PATH "/Interfaces"
#define WPAS_DBUS_IFACE_INTERFACE WPAS_DBUS_INTERFACE ".Interface"
#define WPAS_DBUS_NETWORKS_PART "Networks"
#define WPAS_DBUS_IFACE_NETWORK WPAS_DBUS_INTERFACE ".Network"
#define WPAS_DBUS_BSSIDS_PART "BSSIDs"
#define WPAS_DBUS_IFACE_BSSID WPAS_DBUS_INTERFACE ".BSSID"
int running = 1;
void stopLoop(int sig)
{
running = 0;
}
void sendScan()
{
// TODO !
}
void loop(DBusConnection* conn)
{
DBusMessage* msg;
DBusMessageIter args;
DBusMessageIter subArgs;
int argType;
int i;
int buffSize = 1024;
char strValue[buffSize];
const char* member = 0;
sendScan();
while (running)
{
// non blocking read of the next available message
dbus_connection_read_write(conn, 0);
msg = dbus_connection_pop_message(conn);
// loop again if we haven't read a message
if (!msg)
{
printf("No message received, waiting a little ...\n");
sleep(1);
continue;
}
else printf("Got a message, will analyze it ...\n");
// Print the message member
printf("Got message for interface %s\n",
dbus_message_get_interface(msg));
member = dbus_message_get_member(msg);
if(member) printf("Got message member %s\n", member);
// Check has argument
if (!dbus_message_iter_init(msg, &args))
{
printf("Message has no argument\n");
continue;
}
else
{
// Go through arguments
while(1)
{
argType = dbus_message_iter_get_arg_type(&args);
if (argType == DBUS_TYPE_STRING)
{
printf("Got string argument, extracting ...\n");
/* FIXME : got weird characters
dbus_message_iter_get_basic(&args, &strValue);
*/
/* FIXME : segmentation fault !
dbus_message_iter_get_fixed_array(
&args, &strValue, buffSize);
*/
/* FIXME : segmentation fault !
dbus_message_iter_recurse(&args, &subArgs);
*/
/* FIXME : deprecated!
if(dbus_message_iter_get_array_len(&args) > buffSize)
printf("message content to big for local buffer!");
*/
//printf("String value was %s\n", strValue);
}
else
printf("Arg type not implemented yet !\n");
if(dbus_message_iter_has_next(&args))
dbus_message_iter_next(&args);
else break;
}
printf("No more arguments!\n");
}
// free the message
dbus_message_unref(msg);
}
}
int main(int argc, char* argv[])
{
DBusError err;
DBusConnection* conn;
int ret;
char signalDesc[1024]; // Signal description as string
// Signal handling
signal(SIGKILL, stopLoop);
signal(SIGTERM, stopLoop);
// Initialize err struct
dbus_error_init(&err);
// connect to the bus
conn = dbus_bus_get(DBUS_BUS_SYSTEM, &err);
if (dbus_error_is_set(&err))
{
fprintf(stderr, "Connection Error (%s)\n", err.message);
dbus_error_free(&err);
}
if (!conn)
{
exit(1);
}
// request a name on the bus
ret = dbus_bus_request_name(conn, WPAS_DBUS_SERVICE, 0, &err);
if (dbus_error_is_set(&err))
{
fprintf(stderr, "Name Error (%s)\n", err.message);
dbus_error_free(&err);
}
/* Connect to signal */
// Interface signal ..
sprintf(signalDesc, "type='signal',interface='%s'",
WPAS_DBUS_IFACE_INTERFACE);
dbus_bus_add_match(conn, signalDesc, &err);
dbus_connection_flush(conn);
if (dbus_error_is_set(&err))
{
fprintf(stderr, "Match Error (%s)\n", err.message);
exit(1);
}
// Network signal ..
sprintf(signalDesc, "type='signal',interface='%s'",
WPAS_DBUS_IFACE_NETWORK);
dbus_bus_add_match(conn, signalDesc, &err);
dbus_connection_flush(conn);
if (dbus_error_is_set(&err))
{
fprintf(stderr, "Match Error (%s)\n", err.message);
exit(1);
}
// Bssid signal ..
sprintf(signalDesc, "type='signal',interface='%s'",
WPAS_DBUS_IFACE_BSSID);
dbus_bus_add_match(conn, signalDesc, &err);
dbus_connection_flush(conn);
if (dbus_error_is_set(&err))
{
fprintf(stderr, "Match Error (%s)\n", err.message);
exit(1);
}
// Do main loop
loop(conn);
// Main loop exited
printf("Main loop stopped, exiting ...\n");
dbus_connection_close(conn);
return 0;
}
Any pointer to any nice, complete, low-level C tutorial is strongly appreciated! I'm also planning to do some remote method call, so if the tutorial covers this subject it would be great! Saying I'm not very smart because I don't get it with the official tutorial is also appreciated :-p!
Or is there another way to communicate with wpa_supplicant (except using wpa_cli)?
EDIT 1:
Using 'qdbusviewer' and the introspection capabilty, this helped me a lot discovering what and how wpa_supplicant works using dbus. Hopping that this would help someone else!
Edit 2:
Will probably come when I'll find a way to read string values on D-Bus!
You have given up the tools that would help you to learn D-Bus more easily and are using the low level libdbus implementation, so maybe you deserve to be in pain. BTW, are you talking about ARM, like a cell phone ARM ? With maybe 500 Mhz and 256 MB RAM ? In this case the processor is well suited to using glib, Qt or even python. And D-Bus is most useful when you're writing asynchronous event driven code, with an integrated main loop, for example from glib, even when you're using the low level libdbus (it has functions to connect to the glib main loop, for example).
Since you're using the low level library, then documentation is what you already have:
http://dbus.freedesktop.org/doc/api/html/index.html
Also, libdbus source code is also part of the documentation:
http://dbus.freedesktop.org/doc/api/html/files.html
The main entry point for the documentation is the Modules page (in particular, the public API section):
http://dbus.freedesktop.org/doc/api/html/modules.html
For message handling, the section DBusMessage is the relevant one:
DBusMessage
There you have the documentation for functions that parse item values. In your case, you started with a dbus_message_iter_get_basic. As described in the docs, retrieving the string requires a const char ** variable, since the returned value will point to the pre-allocated string in the received message:
So for int32 it should be a "dbus_int32_t*" and for string a "const char**". The returned value is by reference and should not be freed.
So you can't define an array, because libdbus won't copy the text to your array. If you need to save the string, first get the constant string reference, then strcpy to your own array.
Then you tried to get a fixed array without moving the iterator. You need a call to the next iterator (dbus_message_iter_next) between the basic string and the fixed array. Same right before recursing into the sub iterator.
Finally, you don't call get_array_len to get the number of elements on the array. From the docs, it only returns byte counts. Instead you loop over the sub iterator using iter_next the same way you should have done with the main iterator. After you have iterated past the end of the array, dbus_message_iter_get_arg_type will return DBUS_TYPE_INVALID.
For more info, read the reference manual, don't look for a tutorial. Or just use a reasonable d-bus implementation:
https://developer.gnome.org/gio/2.36/gdbus-codegen.html
GIO's GDBus automatically creates wrappers for your d-bus calls.
http://qt-project.org/doc/qt-4.8/intro-to-dbus.html
http://dbus.freedesktop.org/doc/dbus-python/doc/tutorial.html
etc.
You don't need to use/understand working of dbus If you just need to write a C program to communicate with wpa_supplicant. I reverse engineered the wpa_cli's source code. Went through its implementation and used functions provided in wpa_ctrl.h/c. This implementation takes care of everything. You can use/modify whatever you want, build your executable and you're done!
Here's the official link to wpa_supplicant's ctrl_interface:
http://hostap.epitest.fi/wpa_supplicant/devel/ctrl_iface_page.html
I doubt this answer will still be relevant to the author of this question,
but for anybody who stumbles upon this like I did:
The situation is now better than all those years ago if you don't want to include GTK/QT in your project to access dbus.
There is dbus API in Embedded Linux Library by Intel (weird I remember it being open, maybe it is just for registered users now?)
and systemd sd-bus library now offers public API. You probably run systemd anyway unless you have a really constrained embedded system.
I have worked with GDbus, dbus-cpp and sd-bus and although I wanted a C++ library,
I found sd-bus to be the simplest and the least problematic experience.
I did not try its C++ bindings but they also look nice
#include <stdio.h>
#include <systemd/sd-bus.h>
#include <stdlib.h>
const char* wpa_service = "fi.w1.wpa_supplicant1";
const char* wpa_root_obj_path = "/fi/w1/wpa_supplicant1";
const char* wpa_root_iface = "fi.w1.wpa_supplicant1";
sd_bus_error error = SD_BUS_ERROR_NULL;
sd_bus* system_bus = NULL;
sd_event* loop = NULL;
sd_bus_message* reply = NULL;
void cleanup() {
sd_event_unref(loop);
sd_bus_unref(system_bus);
sd_bus_message_unref(reply);
sd_bus_error_free(&error);
}
void print_error(const char* msg, int code) {
fprintf(stderr, "%s %s\n", msg, strerror(-code));
exit(EXIT_FAILURE);
}
const char* get_interface(const char* iface) {
int res = sd_bus_call_method(system_bus,
wpa_service,
wpa_root_obj_path,
wpa_root_iface,
"GetInterface",
&error,
&reply,
"s",
"Ifname", "s", iface,
"Driver", "s", "nl80211");
if (res < 0) {
fprintf(stderr, "(get) error response: %s\n", error.message);
return NULL;
}
const char* iface_path;
/*
* an object path was returned in reply
* this works like an iterator, if a method returns (osu), you could call message_read_basic in succession
* with arguments SD_BUS_TYPE_OBJECT_PATH, SD_BUS_TYPE_STRING, SD_BUS_TYPE_UINT32 or you could
* call sd_bus_message_read() and provides the signature + arguments in one call
* */
res = sd_bus_message_read_basic(reply, SD_BUS_TYPE_OBJECT_PATH, &iface_path);
if (res < 0) {
print_error("getIface: ", res);
return NULL;
}
return iface_path;
}
const char* create_interface(const char* iface) {
int res = sd_bus_call_method(system_bus,
wpa_service,
wpa_root_obj_path,
wpa_root_iface,
"CreateInterface",
&error,
&reply,
"a{sv}", 2, //pass array of str:variant (dbus dictionary) with 2
//entries to CreateInterface
"Ifname", "s", iface, // "s" variant parameter contains string, then pass the value
"Driver", "s", "nl80211");
if (res < 0) {
fprintf(stderr, "(create) error response: %s\n", error.message);
return NULL;
}
const char* iface_path;
res = sd_bus_message_read_basic(reply, SD_BUS_TYPE_OBJECT_PATH, &iface_path);
if (res < 0) {
print_error("createIface: ", res);
}
return iface_path;
}
int main() {
int res;
const char* iface_path;
//open connection to system bus - default either opens or reuses existing connection as necessary
res = sd_bus_default_system(&system_bus);
if (res < 0) {
print_error("open: ", res);
}
//associate connection with event loop, again default either creates or reuses existing
res = sd_event_default(&loop);
if (res < 0) {
print_error("event: ", res);
}
// get obj. path to the wireless interface on dbus so you can call methods on it
// this is a wireless interface (e.g. your wifi dongle) NOT the dbus interface
// if you don't know the interface name in advance, you will have to read the Interfaces property of
// wpa_supplicants root interface — call Get method on org.freedesktop.DBus properties interface,
// while some libraries expose some kind of get_property convenience function sd-bus does not
const char* ifaceName = "wlp32s0f3u2";
if (!(iface_path = get_interface(ifaceName))) { //substitute your wireless iface here
// sometimes the HW is present and listed in "ip l" but dbus does not reflect that, this fixes it
if (!(iface_path = create_interface(ifaceName))) {
fprintf(stderr, "can't create iface: %s" , ifaceName);
cleanup();
return EXIT_FAILURE;
}
}
/*
call methods with obj. path iface_path and dbus interface of your choice
this will likely be "fi.w1.wpa_supplicant1.Interface", register for signals etc...
you will need the following to receive those signals
*/
int runForUsec = 1000000; //usec, not msec!
sd_event_run(loop, runForUsec); //or sd_event_loop(loop) if you want to loop forever
cleanup();
printf("Finished OK\n");
return 0;
}
I apologize if the example above does not work perfectly. It is an excerpt from an old project I rewrote to C from C++ (I think it's C(-ish), compiler does not protest and you asked for C) but I can't test it as all my dongles refuse to work with my desktop right now. It should give you a general idea though.
Note that you will likely encounter several magical or semi-magical issues.
To ensure smooth developing/testing do the following:
make sure other network management applications are disabled (networkmanager, connman...)
restart the wpa_supplicant service
make sure the wireless interface is UP in ip link
Also, because is not that well-documented right now:
You can access arrays and inner variant values by sd_bus_message_enter_container
and _exit counterpart. sd_bus_message_peek_type might come handy while doing that.
Or sd_bus_message_read_array for a homogenous array.
The below snippet works for me
if (argType == DBUS_TYPE_STRING)
{
printf("Got string argument, extracting ...\n");
char* strBuffer = NULL;
dbus_message_iter_get_basic(&args, &strBuffer);
printf("Received string: \n %s \n",strBuffer);
}

How to Fire Windows Service stop event on System restart?

I have written Windows Service in VC++ to mount Drives on System restart.
Now when i restart the system, on system shutdown i want to fire my service stop event which is not getting fired.
I have set Windows service properties as automatic but it does not work.
When i manually click on stop button stop event get fired.
Any help is apprecaited.
My code looks like :
void WINAPI ServiceCtrlHandler(DWORD Opcode)
{
switch(Opcode)
{
case SERVICE_CONTROL_PAUSE:
m_ServiceStatus.dwCurrentState = SERVICE_PAUSED;
break;
case SERVICE_CONTROL_CONTINUE:
m_ServiceStatus.dwCurrentState = SERVICE_RUNNING;
break;
case SERVICE_CONTROL_STOP:
m_ServiceStatus.dwWin32ExitCode = 0;
m_ServiceStatus.dwCurrentState = SERVICE_STOPPED;
m_ServiceStatus.dwCheckPoint = 0;
m_ServiceStatus.dwWaitHint = 0;
Disconnect() ;// This method i want to get called on system shot down automatically.
SetServiceStatus (m_ServiceStatusHandle,&m_ServiceStatus);
bRunning=false;
break;
case SERVICE_CONTROL_INTERROGATE:
break;
}
return;
}
This is relatively straight forward.
Either handle the SERVICE_CONTROL_SHUTDOWN in your current callback handler function by adding another case to the switch statement. Probably something like:
void WINAPI ServiceCtrlHandler(DWORD Opcode)
{
switch(Opcode)
{
case SERVICE_CONTROL_PAUSE:
m_ServiceStatus.dwCurrentState = SERVICE_PAUSED;
break;
case SERVICE_CONTROL_CONTINUE:
m_ServiceStatus.dwCurrentState = SERVICE_RUNNING;
break;
case SERVICE_CONTROL_STOP:
m_ServiceStatus.dwWin32ExitCode = 0;
m_ServiceStatus.dwCurrentState = SERVICE_STOPPED;
m_ServiceStatus.dwCheckPoint = 0;
m_ServiceStatus.dwWaitHint = 0;
Disconnect() ;// This method i want to get called on system shot down automatically.
SetServiceStatus (m_ServiceStatusHandle,&m_ServiceStatus);
bRunning=false;
break;
case SERVICE_CONTROL_INTERROGATE:
break;
case SERVICE_CONTROL_SHUTDOWN:
Disconnect();
break;
}
return;
}
OR:
Instead of registering the callback function with RegisterServiceCtrlHandler use RegisterServiceCtrlHandlerEx. This new callback method is preferred. Your callback function's signature needs to match HandlerEx, see MSDN for more info.
DWORD WINAPI HandlerEx(
__in DWORD dwControl,
__in DWORD dwEventType,
__in LPVOID lpEventData,
__in LPVOID lpContext
);
Add another case to your switch statement, either SERVICE_CONTROL_PRESHUTDOWN (not available on XP/Server 2003) or SERVICE_CONTROL_SHUTDOWN, read the warnings about handling these notifications in the HandlerEx documentation referenced above.

Resources