Why does GetProcAddress() sometimes return an address outside of the DLL?

By , April 14, 2021 5:43 pm

For most uses of GetProcAddress() the address returned will be an address inside the DLL that you’re using to lookup the function.

But sometimes the address returned is not in the DLL. That’s odd! Is this a bug?

It’s not a bug. This intended behaviour. This article explains why it happens.

Before we get into what’s going on, let’s recap what GetProcAddress() does.

What is GetProcAddress()?

GetProcAddress() is a function exported from kernel32.dll.

It’s used for looking up the address of a function exported from a DLL.

The function can be exported by name (ASCII strings only) or by ordinal (an integer between 0 and 65535). Examples of DLLs that export by ordinal are the MFC dlls. Most DLLs export by name.

GetProcAddress() examines the export address table in the DLL looking for a function with the name (or ordinal) specified. If the function is found the address is returned, otherwise NULL is returned.

Here’s a demonstration looking up the address of the StarCrossedLovers(); function in the theatre.dll. This DLL handles all functionality related to the theatre plays needed by the application.

If the function is found it is called with the names of the two lovers, Romeo and Juliet.


HMODULE hModTheatre;

hModTheatre = GetModuleHandle(_T("theatre.dll")); // could be a call to LoadLibrary instead
if (hModTheatre != NULL)
{
    LOVERS_FUNC p;

    p = (LOVERS_FUNC)GetProcAddress(hModTheatre, "StarCrossedLovers");
    if (p != NULL)
        (*p)("Romeo", "Juliet");
}

Most of the time the returned address will be inside the DLL being queried.

Some of the time the returned address is not from the DLL being queried. Where did the address come from?

Where did the address come from?

When the address returned from GetProcAddress() is from outside of the DLL being queried, the address returned is an address inside another DLL that is supplying functions to the original DLL using a process called Forwarding.

To demonstrate this I’m going to examine the NetApiBufferAllocate() function which is exported from netapi32.dll.

If we call GetProcAddress() to look up the address of NetApiBufferAllocate the address returned on my Windows 10 machine is 0x70342800.

But if we look at the load address for netapi32.dll it’s 0x6fcc0000 and it’s size is only 76KB. The address returned is outside of netapi32.dll. If we then look at the other DLLs in the application and find which DLL contains address 0x70342800 we find it’s netutils.dll. Here’s a screenshot from VM Validator showing that.


OK, so we know the exported function address doesn’t come from the DLL we queried (netapi32.dll), but actually comes from netutils.dll. How does that work?

It’s going to be easier if I show two images of the netapi32.dll and netutils.dll while we examine the NetApiBufferAllocate() function.

These images are being viewed using PE File Browser. Both of these images show the DLLs loaded at their preferred load addresses (if you repeat this test with PE File Browser you’ll most likely get the same addresses).

netapi32.dll


netutils.dll


What you can see in the first image is that netapi32.dll has a NULL exported address for NetApiBufferAllocate and the the forwarding column shows NETUTILS.NetApiBufferAllocate. The forwarded function is composed of a DLL name, a period and the name of the exporting function (which doesn’t have to be the same as the original function name).

When you look at the exports for netutils.dll you can see that it exports a function NetApiBufferAllocate with non-NULL address.

What happens when you use GetProcAddress() to fetch the address of an exported function that has been forwarded from another DLL is that GetProcAddress() decodes the forwarded function name and looks up the forwarded function name in the forwarded function DLL. In the case of NetApiBufferAllocate in netapi32.dll that means GetProcAddress() returns the address of NetApiBufferAllocate in netutils.dll.

What’s the purpose of function forwarding?

I think it allows DLLs that were once large repositories for a variety of code to be split into many DLLs, each of which serve one purpose. These DLLs then forward the DLLs to the original DLL which is now acting as a central location (for backward compatibility) to find the functions. Client applications never know the code they are calling now resides in a DLL dedicated to the particular task rather than in the original monolithic DLL. This can be useful for the maintainers of the DLLs, improving maintenance, testing and security of the component DLLs.

If we return to the original example of the StarCrossedLovers() function exported from theatre.dll this could be reimplemented by moving all all Shakespeare related functionality out of theatre.dll into shakespeare.dll. StarCrossedLovers() in shakespeare.dll would then be forwarded to theatre.dll.

You could take it a stage further (sorry, no pun intended!) with each of Shakespeare’s plays being represented by their own DLL which forwards functions to shakespeare.dll (or theatre.dll).

What’s new with Thread Validator

By , December 10, 2020 6:27 pm

There are some changes coming to Thread Validator.

I’m going to describe the various changes and the reasons behind them.

Name Change

The first one is the name change. C++ Thread Validator becomes Thread Validator.

This change is to keep in line with our other tools not having language specific prefixes.

New UX Theme

A new UX theme which is has less visual clutter and is calmer to look at has been introduced. We’ve written about that in New UX Theme.


x64 and x86 support

C++ Thread Validator shipped in two versions, a 32 bit version and a 64 bit version that could also process 32 bit executables.

Thread Validator ships in a 64 bit version that can also process 32 bit executables. On a 32 bit machine the 32 bit version installs, on a 64 bit machine both 64 bit and 32 bit versions install, because occasionally there is a 32 bit native bug that you only deal with from the 32 bit version of the tool.

The reasons for this change are

  • All our other tools ship in versions that support both 64 bit and 32 bit executables. Thread Validator should do the same.
  • 64 processors are the dominant processors in the market. We should support these by default.

Availability

These changes to Thread Validator will be available after 13 December 2020.

What’s new with Performance Validator

By , December 10, 2020 6:26 pm

There are many changes coming to Performance Validator.

I’m going to describe the various changes and the reasons behind them.

Name Change

The first one is the name change. C++ Performance Validator becomes Performance Validator.

Because Performance Validator will be capable of handling multiple technologies and languages having language specific designators prefixing Performance Validator doesn’t make any sense.

New UX Theme

A new UX theme which is has less visual clutter and is calmer to look at has been introduced. We’ve written about that in New UX Theme.


.Net Support

Performance Validator now supports .Net languages. C#, VB.Net, C++.Net, etc.

.Net Performance Validator is discontinued, all of it’s functionality moving into Performance Validator.

x64 and x86 support

C++ Performance Validator shipped in two versions, a 32 bit version and a 64 bit version that could also process 32 bit executables.

Performance Validator ships in a 64 bit version that can also process 32 bit executables. On a 32 bit machine the 32 bit version installs, on a 64 bit machine both 64 bit and 32 bit versions install, because occasionally there is a 32 bit native bug that you only deal with from the 32 bit version of the tool.

The reasons for this change are

  • .Net applications can be built in 32 bit, 64 bit, and Any CPU versions. An Any CPU version launched on a 64 bit machine will run as 64 bit. To provide full .Net support we couldn’t support Any CPU on 64 bit from 32 bit Performance Validator. The sensible option is to only support Performance Validator in a form that can support both 32 bit and 64 bit architectures.
  • 64 processors are the dominant processors in the market. We should support these by default.

New menu items

To support the new .Net functionality there are some additional launch options for working with ASP.Net applications (IIS and Web Development Server) as well as .Net applications and .Net services.


Launch ASP.Net application using IIS.


Launch ASP.Net application using Web Development Server.


New settings options

The settings dialog has two new panels to allow you to configure .Net Function Inlining and .Net Function Caching. The defaults are the values that an application would normally run with.

.Net Function Inlining


.Net Function Caching


New launch option

With the ability to process both native and .Net applications and mixed mode applications comes the desire to sometimes restrict performance profiling to just native code, or just .Net code, or to allow any code (mixed mode) to be profiled. To handle this we’ve added a simple combo dropdown on the various launch dialogs that allows you to choose how performance profiling is handled at a very high level.


Availability

These changes to Performance Validator will be available after 13 December 2020.

What’s new with Memory Validator

By , December 10, 2020 6:26 pm

There are many changes coming to Memory Validator.

I’m going to describe the various changes and the reasons behind them.

Name Change

The first one is the name change. C++ Memory Validator becomes Memory Validator.

Because Memory Validator will be capable of handling multiple technologies and languages having language specific designators prefixing Memory Validator doesn’t make any sense.

New UX Theme

A new UX theme which is has less visual clutter and is calmer to look at has been introduced. We’ve written about that in New UX Theme.


.Net Support

Memory Validator now supports .Net languages. C#, VB.Net, C++.Net, etc.

.Net Memory Validator is discontinued, all of it’s functionality moving into Memory Validator.

x64 and x86 support

C++ Memory Validator shipped in two versions, a 32 bit version and a 64 bit version that could also process 32 bit executables.

Memory Validator ships in a 64 bit version that can also process 32 bit executables. On a 32 bit machine the 32 bit version installs, on a 64 bit machine both 64 bit and 32 bit versions install, because occasionally there is a 32 bit native bug that you only deal with from the 32 bit version of the tool.

The reasons for this change are

  • .Net applications can be built in 32 bit, 64 bit, and Any CPU versions. An Any CPU version launched on a 64 bit machine will run as 64 bit. To provide full .Net support we couldn’t support Any CPU on 64 bit from 32 bit Memory Validator. The sensible option is to only support Memory Validator in a form that can support both 32 bit and 64 bit architectures.
  • 64 processors are the dominant processors in the market. We should support these by default.

New menu items

To support the new .Net functionality there are some additional launch options for working with ASP.Net applications (IIS and Web Development Server) as well as .Net applications and .Net services.


Launch ASP.Net application using IIS.


Launch ASP.Net application using Web Development Server.


New settings options

The settings dialog has four new panels to allow you to configure what types of .Net data are collected, whether to enable stale .Net object detection, if .Net heap dumps should be collected, and how and when .Net memory snapshots should be made.

.Net Collect


.Net Stale Object Detection


.Net Heap Dump


.Net Snapshots


New launch option

With the ability to process both native and .Net applications and mixed mode applications comes the desire to sometimes restrict memory monitoring to just native code, or just .Net code, or to allow any code (mixed mode) to be monitored. To handle this we’ve added a simple combo dropdown on the various launch dialogs that allows you to choose how memory monitoring is handled at a very high level.


New Summary Display


The summary display provides panels for a particular summary topic with key statistics for that topic displayed in the panel. Panels can display bar graphs, circular graphs and timeline graphs as well as data in table format. Most summaries allow you to click on a data item and be taken to a main tab for the data that was clicked on. This allows the summary display to function as a jumping off point to explore data throughout Memory Validator.

New Memory tab

The new memory tab performs the same function as the Memory tab in previous versions of Memory Validator, but now it contains two sub-tabs. One for viewing native allocations (memory and handles), and one for viewing .Net allocations (memory and handles).

New Statistics tab

The Statistics tab is where all the main statistics data is grouped together. Previously this data was in 3 main tabs: Types, Sizes and Locations. Those 3 tabs are now sub-tabs under the statistics tabs. They are joined by two .Net specific tabs: Generations and Ages, which report information about which generation has how many objects and what ages given objects are. The Ages tab can also report information about stale objects if that functionality has been turned on (off by default as CPU intensive).


New .Net tab

A new .Net tab is where you can find 3 new sub-tabs dedicated to understanding .Net specific memory behaviour. The three sub-tabs are .Net Snapshots, .Net Heap Dumps and .Net Leak Analysis.

New Snapshots tab

The Snapshots tab allows you to take memory snapshots and compare them to other memory snapshots creating memory comparisons. Both snapshots and comparisons can be viewed to identify the location where an object was allocated. You can also use the context menu to then jump to that object in a heap dump reference graph.


New Heap Dumps tab

The Heap Dump tab allows you to view heap dumps provided when a garbage collection takes place. Using the reference graph you can see which objects hold a reference to an object, and which objects an object is referencing. Plenty of options are provided to allow you to restrict the amount of data to wade through (so that you don’t end up with a graph full objects you didn’t build – from 3rd party software).


New .Net Leak Analysis tab

The Leak Analysis tab provides you with a powerful query functionality that allows to query the .Net allocation data to find objects that may be leaked, loitering unused in memory due to references that should have been set to null. We provide many predefined queries, but you can easily build your own queries. Combine with stale object detection data for even more power.


New Analysis tab

The old Analysis tab has been renamed Query and is now a sub-tab of the new Analysis tab. This sub-tab is joined by previous main tabs Coverage, Hotspots, Pages and Virtual.

The Hotspots tab has been updated to include hotspots for .Net memory and .Net handles. The hotspots tab has been moved to a sub-tab on the main Analysis tab.

Timeline updates

The Timeline tab has been updated to include data for .Net memory, .Net handles and .Net all allocations to provide an equivalent to the native timeline views.

Availability

These changes to Memory Validator will be available after 13 December 2020.

What’s new with Coverage Validator

By , December 10, 2020 6:26 pm

There are many changes coming to Coverage Validator.

I’m going to describe the various changes and the reasons behind them.

Name Change

The first one is the name change. C++ Coverage Validator becomes Coverage Validator.

Because Coverage Validator will be capable of handling multiple technologies and languages having language specific designators prefixing Coverage Validator doesn’t make any sense.

New UX Theme

A new UX theme which is has less visual clutter and is calmer to look at has been introduced. We’ve written about that in New UX Theme.


.Net Support

Coverage Validator now supports .Net languages. C#, VB.Net, C++.Net, etc.

.Net Coverage Validator is discontinued, all of it’s functionality moving into Coverage Validator.

x64 and x86 support

C++ Coverage Validator shipped in two versions, a 32 bit version and a 64 bit version that could also process 32 bit executables.

Coverage Validator ships in a 64 bit version that can also process 32 bit executables. On a 32 bit machine the 32 bit version installs, on a 64 bit machine both 64 bit and 32 bit versions install, because occasionally there is a 32 bit native bug that you only deal with from the 32 bit version of the tool.

The reasons for this change are

  • .Net applications can be built in 32 bit, 64 bit, and Any CPU versions. An Any CPU version launched on a 64 bit machine will run as 64 bit. To provide full .Net support we couldn’t support Any CPU on 64 bit from 32 bit Coverage Validator. The sensible option is to only support Coverage Validator in a form that can support both 32 bit and 64 bit architectures.
  • 64 processors are the dominant processors in the market. We should support these by default.

New menu items

To support the new .Net functionality there are some additional launch options for working with ASP.Net applications (IIS and Web Development Server) as well as .Net applications and .Net services.


Launch ASP.Net application using IIS.


Launch ASP.Net application using Web Development Server.


New settings options

The settings dialog has two new panels to allow you to configure .Net Function Inlining and .Net Function Caching. The defaults are the values that an application would normally run with.

.Net Function Inlining


.Net Function Caching


New launch option

With the ability to process both native and .Net applications and mixed mode applications comes the desire to sometimes restrict code coverage to just native code, or just .Net code, or to allow any code (mixed mode) to be covered. To handle this we’ve added a simple combo dropdown on the various launch dialogs that allows you to choose how code coverage is handled at a very high level.


Availability

These changes to Coverage Validator will be available after 13 December 2020.

New UX Theme

By , December 8, 2020 11:49 am

We’ve been working on a new, calmer UX Theme for a while.

The aim is to reduce the number of lines, have less visual clutter, better demarcation of boundaries, consistent colour use across all tools, and to make everything just a bit simpler and calmer to look at.

The best way to show you these changes to show before and after images of each change.

Dialog Titles

Before: Sections are indicated by text with a horizontal line next to it.


After: Sections are indicated by bold text in Software Verify blue with no horizontal line.


Settings grids

Before: Vertical and horizontal grid lines are part of the display.


After: Vertical and horizontal grid lines are minimised with the boundaries between adjacent lines indicated by a subtle change of background colour.


Data grids

Before: Vertical and horizontal grid lines are part of the display.


After: Horizontal grid lines are minimised with the boundaries between adjacent lines indicated by a subtle change of background colour. Vertical grid lines are present, but very subtle so as not to intrude on the display.


Grid highlighting

Before: There was no automatic grid highlighting.


After: When you move the mouse over a grid the line under the mouse automatically highlights in light blue. This can be very useful on wide grids with many columns.


The grid highlighting functionality does not change which items are selected in the grid. It is purely a visual aid. The grid highlighting works for both data grids and settings grids.

Tab Headings

Before: Tabs were displayed with the current tab in bold.


After: Tabs are displayed with all tabs in Software Verify blue and the current tab is bold with the orange highlight colour.


Graphics – Circles

Before: Circles were displayed with outlines and pie section separators.


After: Circles are displayed without outlines and with no pie section separators.


Graphics – Bars

Before: Bars were displayed with outlines.


After: Bars are displayed without outlines.


Splitter Windows

Before: The splitter and the edges of the window were highlighted.

After: The splitter is highlighted. The edges are not highlighted.

Toolbar and menu icons

Before: Our previous icon set was 3D and looked a bit tired.


After: The new icon set is flat and uses the Software Verify colour palette.


Conclusion

These changes in isolation probably don’t look like much, but when you see them all together it makes for a more pleasant experience. The effect is magnified when you’re looking at a lot of data – having less clutter. It’s a subtle but important thing.

These changes will be rolled out across all our tools, both free and commercial, in the weeks following 8 December 2020.

Customers that have purchased tools and that have valid software maintenance will be emailed when software updates containing these changes are available for them to download.

Changes to how we display data

By , June 12, 2020 11:59 am

We’ve changed the display of data on all of our tree controls that are used to display callstacks and call trees.

The old method of display, everything was displayed in black text at normal font weight.

The new method of display, different components of the text are displayed in their own colour, with optional bold, italic, underline or strikethrough, with an optional change in font face. In practice, at present we’re only changing the display colour and turning bold on/off.

We’re using this new display technology to leave some items neutral, emphasise some items and de-emphasise other items. This change has made quite a difference in readability of the display. Right now there is no ability for the user of the software to edit these settings to change the prioritisation and colour scheme we’ve chosen. We’ll provide this capability in a future release.

I’m going to show some before and after images so that you can see the difference for each tool.

C++ Memory Validator

For C++ Memory Validator, we’ve changed both function and filename to be displayed in bold. Type, thread name, timestamp, module name, and symbol name are displayed in different colours. Address is displayed in a de-emphasised colour.

Before


After


Before


After


.Net Memory Validator

For .Net Memory Validator, we’ve changed both function and filename to be displayed in bold. Age, Generation, thread name, timestamp, module name, and symbol name are displayed in different colours. Address is displayed in a de-emphasised colour.

Before


After


Before


After


Thread Validator

For Thread Validator, we’ve changed both function and filename to be displayed in bold. Thread name, timestamp, module name, and symbol name are displayed in different colours.

Before


After


Before


After


Performance Validator

For Performance Validator, we’ve changed both function and filename to be displayed in bold. Module name, and symbol name are displayed in different colours.

Before


After


Coverage Validator

For Coverage Validator, the branches, files and directory displays have had a few tweaks to de-emphasis address information.

Before

After

Bug Validator

For Bug Validator, we’ve re-ordered the display information so that the symbol comes before the filename. Both function and filename are displayed in bold. Timestamp, thread name, module name, and symbol name are displayed in different colours.

Before

After

Memory leak in CPngImage

By , June 1, 2020 9:37 am

A memory leak in a surprising place

We’ve recently been doing some work switching our resources in our programs from BMP (using CBitmap) to PNG (using CPngImage).

At some point we got around to dog-fooding, which we do with our tools all the time, and we were surprised to see memory leaks (in the form of HGLOBAL handles) in our tools being reported by C++ Memory Validator.


We took a look and found they were coming from CPngImage::LoadFromBuffer(). Here’s the code, with the leaking lines highlighted.

BOOL CPngImage::LoadFromBuffer(LPBYTE lpBuffer, UINT uiSize)
{
        ASSERT(lpBuffer != NULL);

        HGLOBAL hRes = ::GlobalAlloc(GMEM_MOVEABLE, uiSize); // this line leaks
        if (hRes == NULL)
        {
                return FALSE;
        }

        IStream* pStream = NULL;
        LPVOID lpResBuffer = ::GlobalLock(hRes);             // this line leaks
        ASSERT (lpResBuffer != NULL);

        memcpy(lpResBuffer, lpBuffer, uiSize);

        HRESULT hResult = ::CreateStreamOnHGlobal(hRes, FALSE, &pStream);

        if (hResult != S_OK)
        {
                return FALSE;
        }

        if (CMFCToolBarImages::m_bMultiThreaded)
        {
                CMFCToolBarImages::m_CriticalSection.Lock();
        }

        if (m_pImage == NULL)
        {
                m_pImage = new CImage;
                ENSURE(m_pImage != NULL);
        }

        m_pImage->Load(pStream);
        pStream->Release();

        BOOL bRes = Attach(m_pImage->Detach());

        if (CMFCToolBarImages::m_bMultiThreaded)
        {
                CMFCToolBarImages::m_CriticalSection.Unlock();
        }

        return bRes;
}

Verifying the memory leak

At first we thought C++ Memory Validator might have made a mistake, as it seemed unlikely that such a well used class would contain a mistake like a memory leak.

To check if this was correct memory leak report or a FALSE positive we created a test program where we can repeatedly create and destroy images in rapid succession. If the leak is real the toy program will soon fail to allocate memory. If the leak is a false positive by C++ Memory Validator, the toy program will run forever with no problems. The toy program demonstrated the leak is real – after just over 65,000 loads of an image with CPngImage, all GlobalAlloc() allocations fail.

The test program allows you to repeatedly load a BMP, a PNG into CPngImage and a PNG into an svlPngImage. The image is destroyed after loading. You can specify how many times to perform the load, 1, 10, 100, 1000, 10000, 64K and 100,000 times.


We have verified that his memory leak is present in all versions of CPngImage that ship with all versions of Visual Studio (up to and including VS2019, we haven’t looked at VS2021 yet).

Fixing the memory leak

The fix is to add two lines just before the return bRes; statement.

        ::GlobalUnlock(hRes);
        ::GlobalFree(hRes);

Unfortunately CPngImage loads it data from methods that are not virtual, so we couldn’t replace them in the implementation. We created a drop in replacement for CPngImage called svlPngImage. It’s identical to the CPngImage class with the exception that the CMFCToolBarImages calls are commented out, and the two additional lines to prevent the memory leak are added. If you’d like to use svlPngImage the source code for this drop in replacement is in the download.

Test Program Source Code

You can download the test program source code and project files.

Update, after response from Microsoft

I was surprised when I got a reply saying this couldn’t happen because the second parameter to ::CreateStreamOnHGlobal() was TRUE. I searched my machine again, and it was FALSE. I then searched a different machine and it was TRUE. So most likely this bug did exist (and we’re using the version of Visual Studio that has it) but has been fixed in a more recent service pack. My claim that the bug was in all versions of Visual Studio was incorrect because I was checking for the absence of the memory freeing calls without checking the value passed to ::CreateStreamOnHGlobal() was FALSE. The drop in replacement class is still valid.

If you’re using CPngImage, check CPngImage::LoadFromBuffer() and if the second parameter passed to ::CreateStreamOnHGlobal() is FALSE, use our svlPngImage class instead. Otherwise carry on as you are.

Update 2, after further response from Microsoft

Microsoft has modified this function to improve the error handling to fix a memory leak if ::CreateStreamOnHGlobal() fails, in response to this bug report, which prompted them to reexamine the function. Ironic in that the report I made, although demonstrating a real bug, that bug had already been fixed, but it still prompted a fresh look at the function behaviour.

Detecting memory leaks in ISAPI extensions

By , April 21, 2020 11:51 am

Three weeks ago I wrote about how to setup IIS for use with ISAPI extensions.

Today I’m going to show you how easy it is to monitor memory and handle allocations in IIS, and use that information to detect any memory and handle leaks that may be present in your ISAPI extension.

IIS is a very secure Windows service. The account security for IIS mean that it can’t access most parts of the Windows filesystem or even Windows objects. CGI and ISAPI extensions will only execute if you’ve configured IIS properly (see the above linked article for details). You can’t launch IIS from a tool like Memory Validator, and you can’t inject into IIS from a tool like Memory Validator because of the security constraints. That leaves you the option of using an API to interact with IIS from your tool of choice.

But I don’t want to use an API!

“But I don’t want to use an API” I hear you say. You’re concerned about having to build multiple versions of your ISAPI. One for use with Memory Validator and one for production. That’s a valid concern, but it’s not the case. You can just build one version of your ISAPI that uses Memory Validator and use that. If you haven’t done “Monitor ISAPI…” from the Launch menu prior to loading your ISAPI the Memory Validator dlls won’t be loaded. There is no dependency on Software Verify DLLs, you can ship your DLL without any need to ship the Memory Validator DLLs.

But should you need to examine memory allocation behaviour you can just fire up Memory Validator and get to work without making a special build.

ISAPI API

ISAPI extensions provide 3 APIs for IIS to use:

BOOL WINAPI GetExtensionVersion(HSE_VERSION_INFO *pVer);

BOOL WINAPI TerminateExtension(DWORD	dwFlags);

DWORD WINAPI HttpExtensionProc(EXTENSION_CONTROL_BLOCK *pECB);

GetExtensionVersion() is called when the ISAPI is first loaded. We’ll use this to load Memory Validator into IIS.

TerminateExtension() is called when the ISAPI is unloaded. We’ll use this to tell Memory Validator that the work with IIS is done.

HttpExtensionProc() is used to process any requests made to the ISAPI extension.

Memory Validator API

To add the Memory Validator API to the ISAPI we do the following to the ISAPI source code:


  1. Add two header files:

    #include "svlMVStubService.h"
    #include "svlServiceError.h"
    

  2. We add the following code to GetExtensionVersion(). This includes logging success and failure so that we can identify if anything has gone wrong. Without the logging determining failure inside of IIS is very hard (a debugger and a special build of the Memory Validator DLL is required).

        // load Validator here
    
        svlMVStub_setLogFileName(L"C:\\testISAPIWebsite\\svl_MV_log.txt");
        svlMVStub_deleteLogFile();
    
        SVL_SERVICE_ERROR   errCode;
    #ifdef IS6432
        // x86 with x64 GUI
        errCode = svlMVStub_LoadMemoryValidator6432();
    #else   //#ifdef IS6432
        // x86 with x86 GUI
        // x64 with x64 GUI
        errCode = svlMVStub_LoadMemoryValidator();
    #endif   //#ifdef IS6432
        if (errCode != SVL_OK)
        {
            DWORD   lastError;
    
            lastError = GetLastError();
            svlMVStub_writeToLogFileW(L"C++ Memory Validator load failed. \r\n");
            svlMVStub_writeToLogFileLastError(lastError);
            svlMVStub_writeToLogFile(errCode);
    
            svlMVStub_dumpPathToLogFile();
        }
        else
        {
            svlMVStub_writeToLogFileW(L"C++ Memory Validator load success. \r\n");
    
            errCode = svlMVStub_StartMemoryValidatorForIIS();
    	if (errCode != SVL_OK)
    	{
                DWORD   lastError;
    
                lastError = GetLastError();
                svlMVStub_writeToLogFileW(L"Starting C++ Memory Validator failed. \r\n");
                svlMVStub_writeToLogFileLastError(lastError);
                svlMVStub_writeToLogFile(errCode);
    	}
    
            svlMVStub_writeToLogFileW(L"Finished starting C++ Memory Validator\r\n");
        }
    

    You’ll need to edit the location of the logfile to match the name of your website directory.

  3. We add the following code to TerminateExtension().

        // unload Validator here
    
        svlMVStub_UnloadMemoryValidator();
    


Monitoring memory allocations

Start Memory Validator.

From the Launch menu choose the IIS sub menu and then Monitor ISAPI….

The monitor ISAPI dialog is displayed. Any settings from a previous launch are displayed. Edit the settings appropriately.

You need to identify the ISAPI dll that is being processed, the website directory, the IIS process (choose Any IIS if you don’t know or don’t care), the web browser (the default is Microsoft Edge as it seems more tolerant of data format errors than Chrome) and finally the URL you wish to use to test the ISAPI.

Click OK.

Memory Validator copies some DLLs to the directory containing the ISAPI, sets up various variables that will be used inside the IIS process, resets IIS, and launches the web browser to load the specified URL that will load the ISAPI.

When you’ve finished interacting with the ISAPI, you’ll need to stop IIS. Go to the Launch menu, choose the IIS sub menu then Stop IIS.

When IIS has stopped executing Memory Validator displays “Ready” on the status bar, the Memory tab will show any memory leaks and handle leaks that are present.

A short video of this process is shown below.

Conclusion

We’ve completely reworked our ISAPI support so that you have very little to do, just use the API as shown above and launch using the Monitor ISAPI command.

In comparison, here are some instructions for using Boundschecker with IIS. These are out of date and will no longer work. But look how much you have to do manually.

Setting up ISAPI on IIS 10

By , April 3, 2020 11:27 am

Introduction

OK so it’s 2020 and how many people are developing ISAPI extensions? More than you might imagine. Yeah Ruby on Rails and Rust are all the rage these days, but some people still need to work with ISAPI for a bunch of business reasons. I recently had to setup IIS 10 for work with ISAPI on Windows 10. I read a lot of articles on how to do it. None of them were complete, resulting in reading several articles to get something working so I put this together, mainly for my own benefit (because I really don’t need to spend that much time doing this again!). I’m sharing it so you don’t have to go through this.

There’s an interesting gotcha if you’re developing a 32 bit ISAPI extension. Don’t worry I cover that at the end.

I was trying to get a simple ISAPI extension to work before trying anything else. My guess is most of you are working on legacy code, but a few of you may have been instructed to write a new ISAPI. Here’s a good starting point for a simple ISAPI extension if you haven’t already written one.

Creating an ISAPI extension: https://www.codeproject.com/Articles/1432/What-is-an-ISAPI-Extension

Installing IIS components

IIS components are installed via the Windows features dialog.

In the Windows 10 search box type “Turn Windows features on and off”, when windows shows you the result that matches press return (or click it).



The feature selection box is displayed. Select the items highlighted red in the image shown below. Click OK.



If you’ve already got partway through configuring IIS Manager and have realised you don’t have all the required components installed that’s OK, just install them and then close IIS Manager and reopen it (I found that if I didn’t do that not all the component parts would show in IIS Manager, making finding say ISAPI and CGI Restrictions impossible.

Configuring IIS Manager

Start Internet Information Services Manager.

Website

First of all we need a website to work with. If you’ve already got one skip the next few lines.

Add a test website. Right click on “Sites” in the left hand menu and choose “Add Website…”

Choose a website name. For example: “test”.

Choose a location for the website. For example: C:\testISAPIWebsite

Change the port number (just for testing) so that it doesn’t conflict with any other sites you have. For example: 81.

Handler Mappings

Select the server node on the left hand side and double click click on Handler Mappings on the right hand size.



The handler mappings are displayed.



Right click in empty space and choose “Edit Feature Permissions…”.

The Edit Feature Permissions dialog is displayed. Enable Read, Script and Execute persmissions. When you select the execute check box you’ll notice the entry for ISAPI dlls is added to the displayed Handler Mappings. Click OK.



ISAPI and CGI Restrictions

Select the server node on the left hand side and double click click on “ISAPI and CGI Restrictions” on the right hand size.

Right click in empty space and choose “Add…”.

Add the path to your ISAPI dll, a description and select the check box so that it is allowed to execute. Click OK.



This will place a web.config in the directory that contains the DLL. It will look something like this:

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <system.webServer>
        <handlers accessPolicy="Read, Execute, Script">
            <remove name="ISAPI-dll" />
            <add name="ISAPI-dll" path="*.dll" verb="*" modules="IsapiModule" scriptProcessor="C:\testISAPIWebsite\validate.dll" resourceType="File" requireAccess="Execute" allowPathInfo="true" preCondition="bitness32" />
        </handlers>
    </system.webServer>
</configuration>

32 bit ISAPI extensions

If your ISAPI is 32 bit you’ll need to enable them. Go to application pools (under the server node), select the application pool that your website is in, right click, choose “Advanced Settings…”. Change the “Enable 32-Bit Applications” setting to True.


64 bit ISAPI extensions

If your ISAPI is 64 bit you’ll need to ensure that you haven’t got 32 bit extensions enabled. Go to application pools (under the server node), select the application pool that your website is in, right click, choose “Advanced Settings…”. Change the “Enable 32-Bit Applications” setting to False.

Authentication problems

If when trying to view your web pages you get strange error messages, select the server node on the left then go to “Feature Delegation” and turn any entries that are “Read only” to “Read/Write”. Then restart the server (top of the right hand bar).

Note that I’m assuming you’re working on a Dev machine. If you’re working on a production machine you might want to be a bit less cavalier than just turning all settings to Read/Write – work through them one at a time to find out what you need and change only that.

Trying out the website

If we assume your ISAPI is called validate.dll you should be able to test your ISAPI in a browser using http://localhost:81/validate.dll?12345678

Panorama Theme by Themocracy