Access Violations with SetDLLDirectoryA - genuine api or api bait?

When ever I try to use SetDLLDirectoryA SetDllDirectoryA function (winbase.h) - Win32 apps | Microsoft Docs
I always get an access violation.

Now the reason for doing this, is I want to force where a DLL is loaded from.
There is a reg setting HKLM\system\currentcontrolset\control\session manager\ SafeDLLSearchMode Dword which when created and set to 0 switches off DLL Safe Search.
Dynamic-Link Library Search Order - Win32 apps | Microsoft Docs
Dynamic-Link Library Security - Win32 apps | Microsoft Docs

There is a list of known safe dlls which can be found at HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\KnownDLLs .
which are a few windows dlls, like kernel32.dll, so switching safesearch off would make it possible to use (guessing not tried) a malicious version of the windows dll’s.

SetDllDirectoryA also switches off the SafeSearch, so when I tried to do a proof of concept, all I get is access violations when trying to use this api. Now I should stress this is an offline development machine, and whilst I can see the api call in the dll and create a lib file for it, it just crashes when the code is called, making me wonder if this is some sort fake api call built into the kernel32.dll.

Anyone got any ideas if its a genuine api call or some sort of api bait?

I did see on a MS webpage a comment in a code example that the path can be included for security reasons, negating having to rely on the DLL search order by using GetSystemDirectoryA function (sysinfoapi.h) - Win32 apps | Microsoft Docs and GetWindowsDirectoryA function (sysinfoapi.h) - Win32 apps | Microsoft Docs but I’ve not established if the SxS(Side by Side assemblies - a solution to Dll Hell) which is specified in the app manifest file can still override any specified path.

About Side-by-Side Assemblies - Win32 apps | Microsoft Docs
Side-by-side assembly - Wikipedia

Anyone know if the manifest file can override a path specified in api calls like LoadLibrary(Ex), GetModuleHandle(Ex)? Not tried yet, just curious.

GetModuleHandleA function (libloaderapi.h) - Win32 apps | Microsoft Docs

I honestly can’t tell from this what it is you’re trying to do, but if you’re loading a DLL using LoadLibrary then just use the full path to whatever DLL it is you want.

I’m just trying to use SetDllDirectoryA using the clarion module method, I havent tried the loadlibrary(ex) in all honesty, so I’ll give it go.

The TLDR is, SetDllDirectoryA switches off DLLSafesearch until its nulled, so during this time, its possible to force other programs to load dll’s they dont want to! Its another way of hacking apps with minimal effort!

Edit. Antivirus cant scan pwd protected files and some antivirus cant scan a zip file nested Russian Doll style inside 100 other zip files, and antivirus looks for strings, like the eicar file
Download Anti Malware Testfile – Eicar
EICAR test file - Wikipedia
so it would be possible to load a virus (if some code has even been recognised as a virus and thus has a signature) using the virus split over 2 or more strings at a point where it can execute and the AV if it even spots it can at best only kill the process after some damage has been done. This is why the SetDllDirectoryA is a bit of a loose canon.

If I get time, I’ll do a proof of concept.

So what’s your prototype for SetDllDirectoryA then?
A null pointer and a pointer to a null string are not the same, which is why for decades I’ve always prototyped anything as taking a pointer to use a ULONG, then use Address(Cstring) or 0 (as appropriate) as the parameter

1 Like

The directory to be added to the search path.

If this parameter is an empty string (“”), the call removes the current directory from the default DLL search order.

If this parameter is NULL, the function restores the default search order.

I know, and whilst I was using *cstring, your comment has made me think it might need to be a long pointing to a cstring and then null the long. I’ll give that a go because I think there might be some mileage in this attack vector proof of concept if I can get this dll to work. :thinking:

Edit. If I’m right, I think I’ve found a way to bypass the MS Windows Ransomware protection measures.

When I need to specify a particular .dll I use one of these techniques …

a) LoadLibrary
b) an entry in the manifest file

The former is well documented, but for the latter, I add a line at the bottom of the manifest thus …

 <file name="myFile.dll" loadfrom=".\dlls\myFile.dll" />
 </assembly>

I then place my .dlls in the dlls subdirectory beneath my application.

Without this, Windows just follows the normal rules searching for the .dll in the application directory, system directory, path etc.

2 Likes

So at the moment, I’m using LoadLibrary and GetProcAddress to find out if the api is available as per Operating System Version - Win32 apps | Microsoft Docs

You can test for the presence of the functions associated with a feature. To test for the presence of a function in a system DLL, call the LoadLibrary function to load the DLL. Then call the GetProcAddress function to determine whether the function of interest is present in the DLL. Use the pointer returned by GetProcAddress to call the function. Note that even if the function is present, it may be a stub that just returns an error code such as ERROR_CALL_NOT_IMPLEMENTED.

So libmaker could be seeing this api, and the access violation could be because its a stub but I wouldnt have expected an access violation, unless the kernel32.dll I have here doesnt even return an error_call_not_implemented? Dont know, still investigating.

Now the manifest way is interesting, so I’ll have to see if the manifest can be overridden. I suspect it can before Windows 8.1 but not after, but havent tested, and there’s still the problem of trying to ensure I have genuine window dll’s loaded.

So what’s your prototype for SetDllDirectoryA then?

So what’s your prototype for SetDllDirectoryA then?

So what’s your prototype for SetDllDirectoryA then?

Just paste your code. Does it have RAW.

IS_SetDllDirectoryA(*cstring),Bool,Pascal,Raw,Name('SetDllDirectoryA')

and I had to create a special lib file for it.

I will be changing it to

IS_SetDllDirectoryA(long),Bool,Pascal,Name('SetDllDirectoryA')

in due course in order to be able to pass a null address which may be like

IS_SetDllDirectoryA(<*cstring>),Bool,Pascal,Raw,Name(‘SetDllDirectoryA’)

but I will also need to look in the dissembler window to see if an address is passed for a null cstring variable or if a null is passed for the address re the post below to clarify.

At this stage I was only trying to pass a path to the api, I hadnt got as far as passing a null to cancel it.

I also meet this criteria

To compile an application that uses this function, define _WIN32_WINNT as 0x0502 or later

0x0502 = WinXP SP1 or later.

Edit

So using the Long works and passing an address in a long or simply a zero in a long aka a null works.

IS_SetDllDirectoryA(long),Bool,Pascal,Name('SetDllDirectoryA')

These module definitions below dont work, both give an access violation.

IS_SetDllDirectoryA(*cstring),Bool,Pascal,Raw,Name('SetDllDirectoryA')
IS_SetDllDirectoryA(<*cstring>),Bool,Pascal,Raw,Name('SetDllDirectoryA')

Looking at the assembler window, the cstring module definition method is sending (pushing) an address to the null cstring from the stack register ebx.

The long module definition is pushing a variable (loc:cstringaddress) which contains zero.

The api appears to want just a long in this case whether there is a value in the cstring or not.

There appears to be some caveats to using manifests.

So I’m building what is called an isolated application.
Building C/C++ Isolated Applications | Microsoft Docs

For the Side by Side assembles which can be found in the windows C:\Windows\WinSxS folder

Building C/C++ Side-by-side Assemblies | Microsoft Docs

It is important for a DLL that uses an application manifest to have the manifest embedded as a resource with ID equal to 2. If the DLL is dynamically loaded at runtime (for example, using the LoadLibrary function), the operating system loader loads dependent assemblies specified in the DLL’s manifest. An external application manifest for DLLs is not checked during a LoadLibrary call. If the manifest is not embedded, the loader may attempt to load incorrect versions of assemblies or fail to find to find dependent assemblies.

TLDR, Loadlibrary ignores external manifests.
Manifest has to be embedded in order to avoid loading the wrong external manifest file.

Files specified in the manifest without a path found in WinSxS folder.
Files specified in the manifest with a path found accordingly.

Nothing to say whether SetDllDirectoryA over rides the manifest where paths are specified though.

This is the search order for files with embedded manifests, which is different to the loadlibrary(ex) search order.
Assembly Searching Sequence - Win32 apps | Microsoft Docs

I’ve found this in loadlibrary(Ex)(A|W)

If you call LoadLibrary(Ex) with the name of an assembly without a path specification and the assembly is listed in the system compatible manifest, the call is automatically redirected to the side-by-side assembly.

But in order to install into the SxS folder, the dll needs an embed manifest to meet the criteria for getting into the folder.

So then there is the matter of whether a path in the manifest is absolute or relative which this link appears to shed some light on.
c++ - DLL redirection using manifests - Stack Overflow

This post is suggesting Loadibrary will automatically search the installation folder for a dll specified in the manifest without a path.

Still no mention of where SetDllDirectoryA fits in though, like does it override any manifest paths or not?

I think this will be a case of having to create and embed a manifest with a path and see what happens, and then there are the knowndll’s ie windows system dll’s to test, otherwise this could come into play…
Intercepted: Windows Hacking via DLL Redirection - EH-Net Online Mag (archive.org)

Edit

Another thing to check to see if it affects the manifest file assemblys and api call SetDLLDirectoryA is the registry entry
HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager
SafeDLLSearchMode DWORD 0 or 1

I cant believe MS would make it so difficult for an app to ensure its loading the right dll’s.

Havent seen any mention of this registry setting in any of the docs either.

Attached program works for me with both variants of the SetDLLDirectory’s prototype. Actual parameters are pushing to the stack correctly.
test.clw (650 Bytes)

Yeah, I found the problem, I had two pascal’s in the api definition, the rest of it was perfect, a bit of word blindness I think, but thanks. :grinning:

Thank you for the code!

You can paste Code like that into the response. Like GitHub MarkDown type Triple Backticks ``` on a Line Above and Line Below to …

format it as Code. 

There is a </> button on the Toolbar for Code Format but it indents to note code. I find it easily messed up by edits. I prefer to type the 3 backticks as it always always works and is not messed up by copy/paste within the code lines.

               PROGRAM
               MAP
                 Test()
                 MODULE('')
                   SetDLLDirectory (<CONST *CSTRING>),BOOL,PROC,PASCAL,NAME('SetDllDirectoryA')
!alternative       SetDLLDirectory (<*CSTRING>),BOOL,PROC,PASCAL,RAW,NAME('SetDllDirectoryA')
                 END
               END

TestPath1      CSTRING('C:\TEMP'),PRIVATE
TestPath2      CSTRING(''),PRIVATE
  CODE
  Test()
  RETURN
! ==========================================================================

Test  PROCEDURE()
  CODE
  SetDLLDirectory (TestPath1)
  SetDLLDirectory (TestPath2)
  SetDLLDirectory ()
  RETURN

The code formatting does preserve Quotes as CHR(39) otherwise they are changed to typesetter quotes so the code pasted into Clarion does not work. E.g. below lines with quotes:

TestPath1 CSTRING(‘C:\TEMP’),PRIVATE
TestPath2 CSTRING(’’),PRIVATE

One other advantage … the Test.clw attachment will not easily display on my phone or Android tablet, it wants to download.