Tuesday, January 28, 2014

Y U Phish Me? [Part 2]

By Melissa Augustine.

In the last blog post we had done some research on a spear phishing email I received. We used vim and regex to make our lives a bit easier for analysis purposes and we have extracted a suspicious URL.

What’s left to do… well, go to the URL of course!

Paros Proxy

For this instance I am using Paros Proxy, but please my no means assume that is the proxy you must use. There are quite a few proxies out there so use whichever one you are comfortable with. Don’t forget to modify your browser settings to the proxy port so you don’t miss any traffic!

There is a spiffy basic howto for using Paros here.

For this exercise, I want to ensure that I am capturing both requests and responses. This means everytime I send a request from my web browser, or recieve a response from the server, Paros will catch it and allow me to decide to pass or drop the traffic.

All right lets go! Simply enter our suspicious domain hxxp://hoabinhltd.com in the browser and click pass in Paros to see what we get back:

 HTTP/1.1 302 Moved Temporarily
Date: Mon, 13 Jan 2014 23:58:06 GMT
Server: Apache
X-Powered-By: PHP/5.2.17
Location: http://www.cieneguilla.com/.www.paypal.com/.Update/
Content-Length: 0
Connection: close
Content-Type: text/html

Well then! It looks like we have a redirect here! This is taking us to cieneguilla.com. We know malicious actors like to give us analysts (and our detection devices) a hard time by doing things like this… thanks! It’s also interesting that we can see the server type (Apache) and web server type (PHP version 5.2) Ok, now we need to pass through paros the new GET request.


Let’s get some information on this new domain, cieneguilla.com.

 $nslookup cieneguilla.com

Non-authoritative answer:
Name: cieneguilla.com

From CentralOps.net:

 Queried whois.networksolutions.com with "cieneguilla.com"...
Registry Domain ID:  
Registrar WHOIS Server: whois.networksolutions.com
Registrar URL: http://www.networksolutions.com/en_US/
Updated Date: 2012-04-12T00:00:00Z
Creation Date: 2007-04-29T00:00:00Z
Registrar Registration Expiration Date: 2014-04-27T00:00:00Z
Registrar IANA ID: 2
Registrar Abuse Contact Email: abuse@web.com
Registrar Abuse Contact Phone: 1-800-333-7680
Domain Status: clientTransferProhibited
Registry Registrant ID: 
Registrant Name: Huanca, Italo
Registrant Organization: PUBLITOUR
Registrant Street: Av. San Martin Zona D LtD-15, Cieneguilla
Registrant City: Lima
Registrant State/Province: Lima
Registrant Postal Code: Lima 40
Registrant Country: PE

Following the HTTP Session

We get a few more redirects, but we eventually get to some web content for us to view. What I found interesting about the different responses received (different technologies, different content-types).

 HTTP/1.1 302 Moved Temporarily
Date: Mon, 13 Jan 2014 23:58:49 GMT
Server: Apache
X-Powered-By: PHP/5.2.9
location: cf5de2ce4e96c1b32c29ab46a8e8eaa6
Vary: User-Agent,Accept-Encoding
Content-Length: 0
Content-Type: text/html

And another:

 HTTP/1.1 301 Moved Permanently
Date: Mon, 13 Jan 2014 23:59:17 GMT
Server: Apache
Location: http://www.cieneguilla.com/.www.paypal.com/.Update/cf5de2ce4e96c1b32c29ab46a8e8eaa6/
Vary: Accept-Encoding
Content-Length: 292
Content-Type: text/html; charset=iso-8859-1

Here's what the final page looks like rendered:

What we are presented with is a PayPal login. It looks a bit lacking on the content side, no HTTPS is enabled (most sites have you in a secure session during log in… or at least I hope so), and its quite obvious that the URL is not PayPal. Nevertheless, lets put in some data and see what happens. Paros stops the post request.

Well it looks like PHP (Snd1.php) is being passed my login parameters. So now these nice people have poor Rick Suave’s PayPal information. Looking at the tabs/address bar you see they even use the Paypal logo (but not not try to hide the domain name difference) in an attempt to trick the user into believing they are interacting with PayPal.

I also notice the long alphanumeric string in the domain name changes everytime I visit the page. This must be my unique identifier. I ran it against HashID and it guessed it to be an MD5 hash.

I bet everyone can figure out what happens next… Here is Rico’s address and credit card information (not even verified by the server to be a valid credit card number).

Here's the web page asking for address:

And Paros catching the request:

The web page next asks for the card info:

And Paros catching the request:

After all of this, the site kindly redirects you to the real PayPal. If you still have Paros running at this point, you get some certificate issues because the REAL PayPal establishes an SSL connection and Paros uses its own certificate so it can see inside HTTPS traffic:


This was an example of a classic phishing campaign. Sometimes these actors like to throw some malware at you just to ensure they ensnare you, however this one was a simple harvester.

Tuesday, January 21, 2014

Y U Phish Me? [Part 1]

By Melissa Augustine.

Some emails have been censored for your protection :)

A few days ago while I was browsing my inbox, I came across an interesting email from "Paypal" with the subject of "Help Centre!". Something didn't look right. Here's the Email:

I was a bit suspicious of this, but at first blush it looked pretty OK. Then I looked at the email header:

support@paypal.com via shepard.sypherz.com? I'm pretty sure Paypal hasn't decided to send email through a third party, something is odd here.

SMTP Header

Lets take a look more into the header. You can click on the down arrow (near the "Reply" button on the new Gmail interface and click 'Show Original') to see the full header.

When analyzing SMTP Header data you start at the bottom (1st action) and work up to the time (most recent action):

Let's dig into each of the numbered areas of this header:

  1. This section shows the actual sending of the email from shepard.sypherz.com. Postfix is an open source mail agent. Also note the time and date, January 11, 2014 09:5501 (-0600). This gives us an idea of the timezone of the offending server and we can do some DNS lookups to try and find anything interesting about it.
  2. Here we see something about 'authentication results' and an SPF string. SPF stands for Sender Policy Framework and was meant to detect spam and spoofing. It did this by verifying the senders IP address.

    From Wikipedia:
    "SPF allows administrators to specify which hosts are allowed to send mail from a given domain by creating a specific SPF record (or TXT record) in the Domain Name System (DNS)".

    So basically if I am trying to relay mail through a non-authorized IP and SPF is enabled, it would be blocked. We see here though the IP address (www-data@shepard.sypherz.com, another domain name) was vouched by its DNS. Well that’s nice. We can do an nslookup to see what we can about that IP:

    This looks like a potential NL IP? Geo-IP confirms this.

  3. Next we see it getting handed over to google.com. We see another server name which we saw earlier with nslookup, nexus.sypherz.com. The time here is 07:55:03 -0800 (PST). This is the time zone for Mountain View, CA
  4. We then see it bouncing around Google (its 10.x.x.x which means its internal servers)

Ok, so what that means is... well, its not from Paypal at all :)

Email Addresses

I can also see (just take my word for it) that there are a decent amount of email addresses in here. I copy and paste them out to a text file, but they are not on one per line so its hard for me to do much with it (I like order). Vim provided a great solution:

/[ ]

The above code is two separate commands run within vim. The first one searches for whatever is in the square brackets, in our case, a space. The 2nd says to search (/s) and replace the space (if you put nothing in here it takes the last search) in every instance (the %) with a carriage return (r&). If that doesn't make sense, look here. I still had to go through and fix a few email addresses up (I think it was due to just addresses hitting the edge and continuing on the next line), but in the end, a lot less work than doing it all by hand!

Now that that’s done, lets see what we got.

$wc -l address.txt
>> 2887 adress.txt

$ sort address.txt | uniq address.txt > uniqaddress.txt
$ wc -l uniqaddress.txt
>> 2887 uniqaddress.txt

So all unique addresses, and looked like it was part of a larger list. I say this because it started with m’s and ended at z’s and the list pre-edited looked sorted already.


Ok great, let’s scroll down the original text to view the message body. You can see the HTML here

Looking at it, it’s hard to see what you are looking for, but let's focus on domains again. I'll copy out the body and save as a file (body.txt). Next some command line magic to pull out all domains with "http".

$ grep -E -o "http://[a-zA-Z0-9.-_]+" body.txt 
>> http://images.paypal.com/en_US/i/logo/logo_emailheader_113wx46h.gif
>> http://images.paypal.com/en_US/i/scr/pixel.gif
>> http://images.paypal.com/en_US/i/scr/scr_emailTopCorners_580wx13h.gif
>> http://images.paypal.com/en_US/i/scr/pixel.gif
>> http://hoabinhltd.com/
>> http://images.paypal.com/en_US/i/scr/pixel.gif
>> http://images.paypal.com/en_US/i/scr/scr_emailBottomCorners_580wx13h.gif
$ grep -c http body.txt 
>> 6

Whew! Let’s go through the 1st grep shall we?

  • grep - pretty obvious, invokes the command grep
  • -E - this will interpret the pattern as an Extended regular expression
  • -o - prints only the matching part of the line (not the whole line which is default)
  • “[stuff]” - this is our regex
    • http:// - find me anything with ‘http://
    • [a-zA-Z0-9.-_]+ - in addition, find me one or more (that’s the +) instances of alphanumeric characters (upper and lower), a ‘-‘, or a dot ‘.

I ran the last grep to make sure I didn't miss anything. I see a discrepancy, the count gave me 6 but the regular expression gave me 7... what gives? Well if you look at the man page for grep you see that it counts the lines where the expression was found. So... this means http was found twice on one line. I could have also piped another grep to remove the paypal with the -v option. You can also look for https domains with this as well!

$ grep -E -o "http://[a-zA-Z0-9.-_]+" body.txt | grep -v paypal

Sweet I found instantly a domain I may want to focus on! We can search in vim for the domain to get more context as to when that domain was called.

 <td><span style="color: rgb(8, 68, 130); outline-width: 0px;" <p>Click Update to Confirm Your Account Now <a href="http://hoabinhltd.com/" target=_blank>  update</a></td>

Well this definitely looks like what I would call the suspicious domain! CentralOps provides some information about the domain:

..and we can use Geo-IP as well:

So this is looking like a classic phishing campaign… what happens if we go to this domain? Tune in next time, same blog time… same blog channel!

Tuesday, January 14, 2014

Creating Custom Peach Fuzzer Publishers

by Brad Antoniewicz.

Peach is arguably the most established, freely available fuzzer out there. It has tons of built in functionality to support a huge range of features. While you can data model even the most complex protocols, you can only go so far with a PeachPit before you realize that you just need a custom publisher. In this blog post we'll show how to write and compile a custom publisher so you can spend all your CPU cycles fuzzing the stuff that matters.

When Is It Time?

Since you can do so much with a DataModel and a StateModel, identifying when it's time to transition from a PeachPit to a custom publisher can be tough. To me, it all depends on what you're looking to fuzz. The most common case is your target protocol or file format has multiple levels of encapsulation. Sure, you could easily DataModel this encapsulation, but then you're stuck manually excluding higher level encapsulated data. And in some cases, the encapsulation creates a situation that the DataModel just can't handle.

Here's a sort interesting example I've recently come across. The application implemented it own custom protocol within a TLS tunnel. The tricky part here is that it was all over UDP. So there had to be another layer of encapsulation (XYZ Proto) above TLS but below UDP to keep state of the TLS tunnel, since UDP is stateless. Here's what the encapsulation looked like from a high level:

Now if we're just looking to fuzz XYZ Proto then a DataModel here using the UDP Publisher would do just fine. However, since we're looking to fuzz Custom Protocol, we have a bit of work to do. Establishing a TLS tunnel is beyond the purpose of the DataModel - and the only way for us to get at the important part, is to buld a custom publisher.

If you're just dealing with file formats, this same idea still applies, but its more likely you can build out the DataModel for the entire file format, rather then hitting the TLS brick wall. That being said, it might not be necessary to build the DataModel for the higher level file formats if a custom publisher can be written.

Compiling Peach

Technically, you don't have to compile Peach from source. A little later on I'll walk you through compiling your custom Publisher without the entire Peach source code. But the reality is that when you're building your Publisher, you'll need to look at the source of other Publishers to get better understanding of how everything works, so you might as well learn to build everything from source anyway.

Download the source package from Peach's sourceforge page. I'd recommend downloading the latest Beta source code, rather then the stable source so that you can take advantage of bug fixes, etc..

To compile from source is as simple as it gets due to a handy install script:

root@kali:~/peach-3.1.53-source# ./waf configure
root@kali:~/peach-3.1.53-source# ./waf build
root@kali:~/peach-3.1.53-source# ./waf install

Peach will install the compiled binaries into output/linux_x86_release/bin and output/linux_x86_debug/bin.

Publisher Structure

Publishers are located within the Peach.Core/Publishers directory of the source package. There are a number available for you to use a reference. Basically every publisher inherits from the Publisher class (Peach.Core/Publisher.cs) and should override a few key functions that are tied back to the corresponding Action Types referenced in the PeachPit. The following table provides a summary of those functions (all are of type protected virtual void unless otherwise noted and descriptions are from the Publisher.cs source)

Function Description
OnStart() Called when the publisher is started. This method will be called once per fuzzing "Session", not on every iteration.
OnStop() Called when the publisher is stopped. This method will be called once per fuzzing "Session", not on every iteration.
OnOpen() Open or connect to a resource. Will be called automatically if not called specifically.
OnClose() Close a resource. Will be called automatically when state model exists. Can also be called explicitly when needed.
OnAccept() Accept an incoming connection.
OnInput() Read data
OnOutput(BitwiseStream data) Send data
protected virtual Variant OnCall(string method, List args) Call a method on the Publishers resource
OnSetProperty(string property, Variant value) Set a property on the Publishers resource.
protected virtual Variant OnGetProperty(string property) Get value of a property exposed by Publishers resource

Depending on the purpose of the Publisher, some of the above functions are more important then others. For instance, if we're only concerned with modifying the output of data right before its sent, then we'd just override OnOutput().

Getting Started

From here on out we'll demonstrate everything else you need to get started by building a simple example that adds a layer of encapsulation within the UDP protocol. This example would be trivial to add to a DataModel, but for the sake of this example, we'll implement it in a Publisher.

First up we'll start out by making a copy of the UdpPublisher which extends the SocketPublisher class:

root@kali:~/peach-3.1.53-source/Peach.Core/Publishers# cp UdpPublisher.cs MyCustomPublisher.cs

We'll set the name for our Publisher that will be referenced in the PeachPit by replacing "Udp" with "MyCustomPublisher on line 35:

[Publisher("MyCustomPublisher", true)]

And name the class of our publisher by replacing "UdpPublisher" with "MyCustomPublisher" on line 43:

 public class MyCustomPublisher: SocketPublisher

and line 49:

 public MyCustomPublisher(Dictionary%lt;string, Variant> args

And that's it! We have our custom publisher all done! Granted, its really a waste at this point since its exactly the same thing as the UdpPublisher, but nonetheless it's still custom :)

Extending Functionality

To make this example a little more interesting, lets add that layer of encapsulation, something like this:

Here we care about fuzzing Custom, but not ABC Proto. So we'd create a custom Publisher to handle ABC Proto and a DataModel for fuzzing Custom. Let's say ABC Proto is structured this way:

First thing we'll need to do in our new Publisher is override the OnOutput function so that we can modify the data before its sent. So we'll add a new line after line 72 and insert:

protected override void OnOutput(BitwiseStream data)


Now comes our program body, we'll need to build a new packet with the ABC Proto's header and length fields. Header is a 2 byte static value of 1234, and length is a 2 byte value for the length of the data field in network byte order. Since the length field is only 2 bytes, we first need to put in some intelligence that ensures the length of data does not exceed the maximum value of that field.

int totalPktLen = (int)data.Length + 4;

if (totalPktLen > 65535) {
    Logger.Debug("ABC Proto Max Packet Length Reached, capping at 65535");
    totalPktLen = 65535;

if ( totalPktLen <= 0 ) {
    Logger.Debug("ABC Proto Min PacketLength Reached, just setting to 4 to account for header and length fields");
    totalPktLen = 4;

This can be an controversial move and its an important note to make about custom publishers. Our intention is to fuzz the heck out of Custom Protocol and as part of that fuzzing, we should be trying really long strings and other values. By implementing this limitation we are effectively limiting our test cases. It might be worthwhile just to forget about an accurate value in the ABC Proto Length field as it might lead to more vulnerabilities.

That being said, let's leave that option up to the end user of the publisher. We'll do that via a parameter that we'll implement a little further down below.

Next we'll create our ABC Proto Start Header which is just a constant 1234:

byte[] abcProtoHdr = { 0x12, 0x34 } ;

Our final product will be a buffer containing the original data packet encapsulated within ABC Proto, so here we'll create that buffer:

var buffer = new byte[totalPktLen];

Now we'll build our packet by first copying the ABC Proto Header into the buffer:

Array.Copy(abcProtoHdr, 0, buffer, 0, abcProtoHdr.Length);

Next we'll handle the length field. It needs to be in network bit order, so we'll do with Array.Reverse() after we copy it to the output buffer:

Array.Copy(BitConverter.GetBytes(totalPktLen - 4), 0, buffer, abcProtoHdr.Length, sizeof(ushort));
Array.Reverse(buffer, abcProtoHdr.Length, sizeof(ushort));

To wrap up the buffer we'll just copy over the original data:

data.Read(buffer, abcProtoHdr.Length + sizeof(ushort), buffer.Length - 4);

At this point we've built-in that ABC Proto layer of encapsulation, since that's all we really needed to do, we can pass that data to the original OnOutput() function that SocketPublisher implements to send:

base.OnOutput(new BitStream(buffer));

Passing Parameters

Ok back to that ABC Proto Length issue we ran into earlier on. We could either restrict the data length and limit our fuzzing or just ignore it. The best approach might be to allow the user make that decision via a Parameter passed to the publisher. To do this we'll need to create a new parameter by inserting a new line after line 51 and providing:

[Parameter("StrictLength", typeof(bool), "Enforce the ABC Proto Length Restrictions (may limit fuzz cases)", "true")]

Here we have StrictLength as a boolean option, set to true by default. Next we'll need to create local variable for it by inserting a new line after line 62:

public bool StrictLength { get; set; }

And now we can wrap our length adjustment code into an if statement:

 if (StrictLength) {
    if (totalPktLen > 65535) {
        Logger.Debug("ABC Proto Max Packet Length Reached, capping at 65535");
        totalPktLen = 65535;

    if ( totalPktLen <= 0 ) {
        Logger.Debug("ABC Proto Min PacketLength Reached, just setting to 4 to account for header and length fields");
        totalPktLen = 4;

Alright! Our custom ABC Proto publisher is written! On to compiling..

Compiling with dmcs

We could recompile the entire Peach source code as per the instructions above, or using a standard Peach binary release, we can save time by compiling only our new custom publisher. Peach runs on Linux with the help of the mono framework which allows .Net applications to run on a number of different platforms. The dmcs utility is a compiler within mono which we'll use on our Kali installation.

Enter the peach binary release directory with your MyCustomPublisher.cs copied into it, and compile with:

 dmcs MyCustomPublisher.cs -out:MyCustomPublisher.dll -target:library -r:Peach.Core.dll,NLog.dll

If all went well, you should should have a MyCustomPublisher.dll!

Calling from the PeachPit

The last thing we need to do is call our custom publisher from a PeachPit via the <Publisher> tag within our Test definition:

 <Test name="Default">
        <StateModel ref="CustomProtocolOutput"/>

        <Publisher class="MyCustomPublisher">
            <Param name="Host" value=""/>
            <Param name="Port" value="12345"/>

The full PeachPit for this project can be found here.

Testing with Wireshark

We'll run a single instance of our fuzz case and use Wireshark to inspect output on the wire:

 root@kali:~/peach-3.1.53-linux-x86-release# mono peach.exe MyCustomPublisherDataModel.xml -1

[[ Peach v3.1.53.0
[[ Copyright (c) Michael Eddington

[*] Test 'Default' starting with random seed 5136.

[R1,-,-] Performing iteration

[*] Test 'Default' finished.

Now Wireshark doesn't have have a plug-in to parse for our ABC Proto (WTH wireshark dev team?!) - but if we look at the raw data we can see our ABC Proto header, length fields, and included within the data is the content of our DataModel.

Source Code

If you'd like to reference the source code for this project, head over to Github:

Got any tips for creating publishers? Share below in the comments!

Tuesday, January 7, 2014

Unsafe DLL Loading Vulnerabilities

By Muralidharan Vadivel.

A common issue we see in applications is the order in which they import DLLs at runtime. This is referred to as a Load Order Vulnerability that can result in local privilege escalation. It became popular a few years ago after the release of a Microsoft Advisory for a number of Microsoft products. In this blog post we'll dissect the vulnerability, exploitation scenarios, and how to fix it.

First let us try to understand the two different types of unsafe DLL loading vulnerabilities i.e. DLL hijacking and Component Resolution Failure:

What is DLL Hijacking?

A Microsoft article explains it as “When an application dynamically loads a dynamic-link library without specifying a fully qualified path name, Windows attempts to locate the DLL by searching a well-defined set of directories in a particular order, as described in Dynamic-Link Library Search Order. If an attacker gains control of one of the directories on the DLL search path, it can place a malicious copy of the DLL in that directory. This is sometimes called a DLL preloading attack or a binary planting attack. If the system does not find a legitimate copy of the DLL before it searches the compromised directory, it loads the malicious DLL. If the application is running with administrator privileges, the attacker may succeed in local privilege elevation”

In simple terms if an application (e.g. Test.exe) loads a DLL (e,g. foo.dll) by just the name, Windows follows a specific search order depending upon whether “SafeDllSearchMode” is enabled or disabled to locate the legitimate DLL. If an attacker has knowledge of this application, he can place a malicious DLL in its search path with the same name as the legitimate DLL forcing the application to load the malicious DLL, thus leading to remote code execution. SafeDLLSearchMode places the user’s current working directory later in the search order.

Assuming that SafeDllSearchmode is enabled, system searches the directories in the following order:

  1. The directory from which the application loaded.
  2. System directory (C:\Windows\System32).
  3. The 16-bit system directory (C:\Windows\System).
  4. The Windows directory (C:\Windows).
  5. The Current Directory.
  6. Directories that are listed in the PATH variables.

This issue had not been considered a serious threat because it requires local file system access on the victim’s host for successful exploitation. Following section describes some of the realistic attack scenarios:

  1. Combining carpet bombing with unsafe DLL loading: When the victim visits a malicious web page, attackers can make the browser automatically download arbitrary files. This is referred to as Carpet bomb attack. This flaw leads to remote code execution if the vulnerable application checks in the desktop directory first for resolving the DLL. For example, Safari Web Browser was vulnerable to carpet bomb attack. Internet explorer 7 loads sqmapi.dll when it runs, suppose this DLL gets downloaded in the victim’s desktop directory by a carpet bomb attack IE7 loads the malicious DLL and executes arbitrary code.
  2. Sending the victim an archive file containing the shortcut to vulnerable application along with the malicious DLL . Since many vulnerable applications resolve the missing DLL in the startup directory this can be used to load up the malicious DLL upon clicking the shortcut to the vulnerable application. This can also be combined with carpet bombing attack.
  3. Opening a document can load certain files placed in the same directory as the document. Attacker can send an archive containing the document along with a malicious DLL to exploit this kind of behavior.

Component Resolution Failure

This occurs when an application fails to resolve a DLL because the DLL does not exist in the specified path or search directories. If this happens, a malicious Dll with the same name can be placed in the specified path directory leading to remote code execution.

Identifying Load Order Issues

We can identify these issues with the help of process monitor. To use process monitor to examine unsafe DLL loading issues:

  1. Start process monitor
  2. Include the following filters
    • Process Name begins with “Name of the process”
    • Operation is CreateFile
    • Operation is LoadImage
    • Path ends with dll
    • Result is Name Not Found
  3. Exclude the following filters
    • Process Name begins with “Name of the process”
    • Operation is RegQueryValue
    • Operation is RegOpenKey

  4. Start your application and observe process monitor output, look out for dll’s that are being searched for in the current directory, system directory etc. There is a good chance that these Dll’s could be vulnerable. Also identify DLL’s that are not present in the specified directory, this can lead to component resolution failure issue.

  5. Download wab32res.dll from http://www.binaryplanting.com/demo/windows_address_book/
  6. Rename this DLL to one of the Vulnerable one’s identified in step 4 and place them in the appropriate folder
  7. Restart the Vulnerable application and observe if wab32res.dll gets loaded by the application


  1. Wherever possible, specify a fully qualified path when using the LoadLibrary, LoadLibraryEx, CreateProcess or ShellExecute functions.
  2. Consider using DLL redirection or manifest to ensure that your application uses the correct DLL
  3. When using the standard search order, make sure that safe DLL search mode is enabled. This places the user's current directory later in the search order, increasing the chances that Windows will find a legitimate copy of the DLL before a malicious copy
  4. Consider removing the current directory from the standard search path by calling SetDllDirectory with an empty string (""). This should be done once early in process initialization, not before and after calls to LoadLibrary. Be aware that SetDllDirectory affects the entire process and that multiple threads calling SetDllDirectory with different values can cause undefined behavior. If your application loads third-party DLLs, test carefully to identify any incompatibilities
  5. Do not use the SearchPath function to retrieve a path to a DLL for a subsequent LoadLibrary call unless safe process search mode is enabled