Wednesday, 29 February 2012

Microsoft EMET - ASLR Small Print with Base Binaries

A quick post on a small fact we became aware of this week. As you will likely be aware Microsoft publishes the Enhanced Mitigation Experience Toolkit (EMET). In the words of Microsoft, EMET provides the following functionality:
"The Enhanced Mitigation Experience Toolkit (EMET) is a utility that helps prevent vulnerabilities in software from being successfully exploited. EMET achieves this by using security mitigation technologies. These technologies function as special protections and obstacles that an exploit author must defeat to exploit software vulnerabilities. These security mitigation technologies do not guarantee that vulnerabilities cannot be exploited. However, they work to make exploitation as difficult to perform as possible. In many instances, a fully-functional exploit that can bypass EMET may never be developed."
Think of EMET as a massive plaster (band aid) for legacy software that missed the memo about defensive compiler features.

After our analysis of KB2639308 it dawned on us that there is some small print associated with EMET as well.  The small print is to do with the inability to randomize the location of the base binary when applying Address Space Layout Randomization. Due to relocations being omitted, the reason that KB2639308 can't randomize base binaries generally, also applies to EMET. So while you will gain some benefit by virtue of the DLLs being randomized you will still have some executable memory at a static location across runs.

So that's it, nothing to panic about but something to keep in mind..

Friday, 24 February 2012

Looking at Mobile Security Today Compared to What We Knew in 2007

I was recently reminded by a friend of my old Symantec blog posts. The year was 2007, I was working in Advanced Threat Research specializing in mobile and having a ball (with a bit of Windows Vista on the side). As it's Friday and we're reflecting I thought it would be good to revisit some of these old posts and see how we're doing. All of these posts are from 2007 and not 2009 as some of the dates may imply (I left Symantec at the end of 2007) - click the titles for the original posts.

Getting Ready to Respond to Mobile Security Issues

Today we're in a transitional phase. While the pro-active work on mobile security is improving in leaps and bounds, as most major software vendors now have security teams, it did get off to a slow start. Platform mitigations such as code signing, sandboxes, ASLR, XN and compiler protections are becoming de-facto across all platforms even if there are some bumps along the way. The ability to respond to security issues is light years ahead of where we were on the most part for a majority of platform/handset vendors. However, there are still some challenges around the ability to deploy patches due to the need for carrier certification and/or sign-off and general carrier apathy.

Security Issues & COTS Mobile Operating Systems – Some Very Rough Numbers

The majority of issues I documented in 2007 were related to image parsing, Bluetooth, VoIP or SMS/MMS. Semi recently we've still been haunted a little bit by SMS (2009) and the protocols that run on top (2010) but it has got better. The big growth in vulnerability numbers on mobile is COTS technologies ported to mobile such as operating systems, web and complex file parsing technology. In addition the baseband security revolution has firmly taken hold due to German ingenuity and doggedness. A revolution I suspect we can expect to run for at least another decade. We knew at the time conceptuality at least the risk of GSM and other bearer protocol based attacks. Thanks to the Internet (as the Symantec version of the blog post has its image broken) I've managed to recover the old mobile attack surface diagram I produced at the time.

Click for larger version - original (c) Symantec Corporation 2007

If we look at that diagram not much has really changed when we consider the modern mobile device attack surface. I do remember in 2007 the lack of active attacks did create business hurdles to get anything done in terms of being pro-active with defences. The reason for these hurdles? Economics! Without attacks users wont pay for or demand protection - a frustrating business reality when you can see what'll end up happening.

Windows CE/Mobile Rootkits

We didn't predict mass rookit annihilation, but we did get to do some research around them. Lots of platforms have subsequently had numerous tiny (yes tiny - not near apocalyptic meltdown as some quarters of the press would have you believe) 'malicious code problems'. However there is now a massive ace up the industries sleeve before it runs away with itself and gets out of control. App Stores, Worlds, Market Places, Bazaars. These centralized application distribution models are going to be the places these malicious applications will be caught and not AV on your phone. Yes some will slip through, security companies will make lots of noise, but these distribution points have the ability to kill anything they've allowed be installed by accident. Also if you think Google is naive enough to not integrate the VxClass technology they acquired from Zynamics into Google Apps Marketplace then I'm a rubber duck!

Anyway that's it for the reflection on the year that was 2007 in mobile security from my perspecitve. What we knew in 2007 (and before) is still true 5 years later. So keep calm and carry on and be assured we'll get there in the end...

Two Months of Recx SDL, SDLC and Software Risk Related Articles - What you may have missed

We've been working on new a whitepaper around some the challenges that organizations face when they become a victim of their own success at finding vulnerabilities (yes it really does become a problem). While writing the conclusions to the paper we included a summary of the higher-level articles we've published over the last couple of months on practical issues related to Secure Development Life-cycles (SDL / SDLC) and software risk. As it's Friday and to give you enough material to busy at least a good proportion of your day with we thought we'd publish the summary here as well. So without further ado here are the articles we've published since December 21, 2011 that you may have missed:



We hope you enjoy... as always if you need advice, have specific assessment requirements or wish to just have a chat feel free to get in contact.

Wednesday, 22 February 2012

Recx Installer Defence - SDL Support Tool and Code for Windows Software Installer Writers

As we've talked about previously Secure Development Life-cycles are neither easy or cheap, contrary to what you may have heard. This is why we've previously proposed the idea of 'Secure Mindfulness' as a more agile / lightweight way to lessen the likelihood of  the inevitable security car crash without stifling a teams productivity or having to invest Greece's deficit in security.

In order for security to scale we need to get the basics right. With applications developed in native programming languages, such  as C and C++, on modern operating systems such as Microsoft Windows, BSD, OSX and Linux this doesn't mean setting out to eradicate every memory corruption bug (a nigh impossible task in our opinion). Instead this means at a minimum doing those security related activities that have the highest return on investment first. An example of such an activity is utilizing the security defences provided by the operating system and developer tool chain which are cheap. 

Vendors such as Microsoft and RIM (bias disclosure: I was behind this initiative at RIM) have good documentation on the features they provide and how to utilize them. The key to success however is ensuring their adoption.

We recently presented at CRESTCon (Council of Registered Ethical Security Testers) on 'Finding the weak link in Windows binaries' (we'll also being giving the talk at Source Boston). The talk covers the whats, whys and hows of identifying binaries that haven't utilized the low cost features of the developer tool chain or operating system to enhance security. We think these assurance exercises should be undertaken as part of many different types of security assessment, software QA and procurement activities; but that's a subject for another post. At the high-level what we've observed is if a feature has to be turned on as opposed to it being on by default then the likelihood is it won't be used unless the development team are either security aware or it's easy to so.

So to the reason for this post; product installers and turning on some extra security defences in a simple manner. If you're trying to make a secure product, then security activities aren't confined to certain groups of developers. Security starts with the development team who write the product installer. Why? you may ask. Well, in the case of Windows there are three defences that applications can opt into (there are some caveats around the server product line here) via modifications to the registry:
To encourage and ease adoption we've released today, under the BSD license a developer support tool: RECX Installer Defence. Simply put, we're making the process of turning on these features extremely easy. In the download you'll find:
  • RecxInstallerDefence.h - Header file for you to include in your install/product with a simple function to call
  • RecxInstallerDefence.cpp - Source code to  RecxInstallerDefence.exe / example of calling the function
  • RecxInstallerDefence.exe - Command line tool so if you're not a developer, or you want to script your installs or alternatively want to perform host lock down.
For developers  RecxInstallerDefence.h has the following function:

//
// Function : ProtectImage
// Takes : String - Image name i.e. 'Foo.exe'
//   Boolean - Force ASLR on - Windows 7 client
//   Boolean - DLL planting mitigations - Vista onwards
//   Boolean - SEHOP protection - Windows 7 client
// Returns : 0 on success
//   > 0  on failure
//
DWORD ProtectImage(TCHAR *strImageName, BOOL bForceASLR, BOOL bDLLPlanting, BOOL bSEHOP)

You can then call this function as part of your installation program. For the non-developers, companies that script their installer or individuals who want to opt applications in as part of a host lock-down exercise we provide a command line tool. Running the command line tool is as simple as:
RecxInstallerDefence Foo.exe
This will then turn on all three protections. The major difference between the two is that the function gives you granular control of which defences to enable, whilst the command line tool just turns them all on.

For QA teams needing to validate it has worked from the command line do the following (important output in blue text):
C:\>reg query "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\Foo.exe"
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\Foo.exe
MitigationOptions REG_QWORD 0x300
CWDIllegalInDllSearch REG_DWORD 0xffffffff
DisableExceptionChainValidation REG_DWORD 0x0
So that's it, nothing more complex left to do other than run a test cycle to iron out any bugs in your code. The software can be downloaded here, and as always, if you have any questions or feedback please feel free to get in touch.

Monday, 20 February 2012

Microsoft KB2639308 - Experiments and Small Print

Microsoft released an interesting hot-fix for Windows 7 and Windows Server 2008 R2 in KB2639308. This patch in the words of Microsoft:
"provides support for the Force ASLR feature. This makes it possible for applications to forcibly relocate images that are not built with the /DYNAMICBASE linker flag. Applications can enable this feature by using new Image File Execution Options (IFEO)."
Recx are currently working on a soon to be released project to assist developers of software installers make use of free OS provided security features. As part of this project we wanted to integrate this functionality into RECXInstallerDefence.h. During our development we had to test that we were creating the registry key correctly and that the patch was installed and working.

We applaud Microsoft for what they're doing, but during our internal testing we stumbled across some small print which we thought we'd share. Our test case was the following:

#include "stdafx.h"
#include 

DWORD Yoink(TCHAR *strFoo, DWORD dwFoo){
 if(dwFoo>10){
  if(rand()%2 == 1){
   fprintf(stdout,"%s\n",strFoo);
  }
 }
 return 1;
}

int _tmain(int argc, _TCHAR* argv[])
{
 char strFoo[200];

 fprintf(stdout,"%p %p\n",_tmain,&strFoo);
 if(argc>4){
  Yoink(argv[3],argc);
 }
 
 return 0;
} 
//  <-- this is blogger.com helpfully closing tags!

When then linked it with /DYNAMICBASE:NO and verified with dumpbin.exe:
C:\>dumpbin /headers Foo.exe
...snip..
8100 DLL characteristics
NX compatible
Terminal Server Aware
...snip..
As we can't see Dynamic Base under DLL characteristics we know it's not ASLR enabled. Now an important bit of information in KB2639308 says the binary needs relocations to be present, this can be seen if you set the MitigationOptions value to 0x300:
"Note If the value is set to 0x300, images with stripped relocations will not load."
When we were testing with the value set to 0x100 (try, but load anyway if you can't randomize) we weren't seeing any randomization. So we set the value to 0x300 to ensure that everything was O.K. with our test case.
C:\>reg query "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\Foo.exe"
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\Foo.exe
MitigationOptions REG_QWORD 0x300
CWDIllegalInDllSearch REG_DWORD 0xffffffff
DisableExceptionChainValidation REG_DWORD 0x0
What did we start seeing?
The system cannot execute the specified program.
This was a little disconcerting so we fired up the Recx time machine and read An In-Depth Look into the Win32 Portable Executable File Format, Part 2 from 2002 which says:
"In Visual C++ 6.0, the linker omits relocations for EXEs when doing a release build. This is because EXEs are the first thing brought into an address space, and therefore are essentially guaranteed to load at the preferred load address. DLLs aren't so lucky, so base relocations should always be left in, unless you have a reason to omit them with the /FIXED switch. In Visual Studio .NET, the linker omits base relocations for debug and release mode EXE files."
Sure enough, looking at our Visual Studio 2010 produced binary, relocations were indeed omitted:  
C:\>dumpbin /all Foo.exe | findstr reloc
0 file pointer to relocation table
0 number of relocations
0 file pointer to relocation table
0 number of relocations
0 file pointer to relocation table
0 number of relocations
0 file pointer to relocation table
0 number of relocations
At this point we should point out that DLLs do include relocations as they may not be guaranteed to load at the same location so may benefit from the patch. To conclude, in our release project we added /FIXED:NO to the linker command line and then verified that relocations were now present:
C:\>dumpbin /all Foo.exe | findstr reloc
0 file pointer to relocation table
0 number of relocations
0 file pointer to relocation table
0 number of relocations
0 file pointer to relocation table
0 number of relocations
0 file pointer to relocation table
0 number of relocations
.reloc name
0 file pointer to relocation table
0 number of relocations
1000 .reloc
Then we executed the binary and it loaded just fine:
C:\>Foo.exe
00011000 0019FE78
Compare this to the same binary but renamed so the Image Execution Options don't take effect:
C:\>Not-Foo.exe
00401000 0018FE78
We see that the binary loads at its requested base address and is thus not randomized when compared to if the forced ASLR option Image Execution Options is used.

Also while Microsoft specifically state there is no minimum entropy the stack appeared at the same location  between executions (but we didn't reboot) which differs from 'proper' ASLR.

So the small print is:
  • Stripped relocations == no ASLR (Microsoft documented this bit).
  • Main program binaries have a very good chance of not having relocations present and thus won't benefit from it even if the DLLs do (Microsoft didn't document this bit).
Our non scientific sample set included the following previously non ASLR enabled Windows binaries:
  • Putty (32bit) - Putty.exe - 0.60.
  • CamStudio (32bit) - camstudio_cl.exe - 2.6b .
  • Apple Software Update (32bit) - SoftwareUpdate.exe - 2.1.3.
  • 7Zip (64bit) - 7z.exe - 9.20.
None of them had relocations present.

Saturday, 18 February 2012

From the Archives: Printing the SEH Chain from a Running Process on Windows in C

A quick post about some code we came across in our archives originally written in July 2007. Around then we had been reading what Matt Pietrek had written in his excellent overview but there was no self contained code available. The function below takes takes a handle to a process and a thread and then dumps the current SEH chain. At the time we used it in a debug event loop in our fuzzing rig.

int PrintSEH(HANDLE hProc, HANDLE hThread)
{
 CONTEXT Context;

 // Get the full context of the processor 
 Context.ContextFlags = CONTEXT_FULL;
 if(GetThreadContext( hThread, &Context ) == 0){
  fwprintf(stdout,L"[!] GetThreadContext failed!\n");
  return 1;
 }

 // this bit of leetness came from the code breakers journal 
 // http://powerhacker.net/documents/Reverse_Engineering/codebreakers_journal/CBJ-2005-65.pdf
 LDT_ENTRY ldtSel;
 if (!GetThreadSelectorEntry(hThread, Context.SegFs, &ldtSel)){
  fwprintf(stdout,L"[!] GetThreadSelectorEntry failed!\n");
  return 1;
 }

 // http://archives.neohapsis.com/archives/fulldisclosure/2004-10/att-0339/sessmgr.c
 DWORD dwFSBase = (ldtSel.HighWord.Bits.BaseHi << 24 ) | ( ldtSel.HighWord.Bits.BaseMid << 16 ) | ( ldtSel.BaseLow );
 fwprintf(stdout,L"[i] FS:[0] (TIB) is @ 0x%08X\n",dwFSBase);
 
 DWORD dwNext=0;
 DWORD dwHandler=0;
 DWORD dwCount=0;

 // Now walk the structure
 dwNext=dwFSBase; 
 while(dwNext!=0xFFFFFFFF){
  // Read the Current
  if (!ReadProcessMemory(hProc,(LPCVOID)(dwNext+4),&(dwHandler),sizeof(DWORD),NULL) ){
   fwprintf(stdout,L"[!] ReadProcessMemory failed!\n");
   return 1;
  }

  // Read the Next
  if (!ReadProcessMemory(hProc,(LPCVOID)(dwNext),&(dwNext),sizeof(DWORD), NULL) ){
   fwprintf(stdout,L"[!] ReadProcessMemory failed!\n");
   return 1;
  }

  if(dwCount>0){
   fwprintf(stdout,L"[i] Exception Handler [%d] is handled by 0x%08X 0x%08X \n",dwCount,dwHandler,dwNext);
  }
  
  dwCount++;
 }

 if(dwCount==0){
  fwprintf(stdout,L"[i] Not SEH Chain Entries Found! - This could be because it's not populated yet! \n");
 }

 return 0;
}

An example of the output when used in a debug event loop is as follows:

[!*] BreakPoint @ 01007147
[i] FS:[0] (TIB) is @ 0x7FFDF000
[i] Exception Handler [1] is handled by 0x010075BA 0x0007FFE0
[i] Exception Handler [2] is handled by 0x7C839AA8 0xFFFFFFFF
[i] Unsetting breakpoint @ 0x01007147
[i] Re-Setting breakpoint @ 0x01007147
[!*] BreakPoint @ 01002b87
[i] FS:[0] (TIB) is @ 0x7FFDF000
[i] Exception Handler [1] is handled by 0x7E440457 0x0007FE08
[i] Exception Handler [2] is handled by 0x7E440457 0x0007FE5C
[i] Exception Handler [3] is handled by 0x7E440457 0x0007FFB0
[i] Exception Handler [4] is handled by 0x010075BA 0x0007FFE0
[i] Exception Handler [5] is handled by 0x7C839AA8 0xFFFFFFFF
[i] Unsetting breakpoint @ 0x01002b87
[i] Re-Setting breakpoint @ 0x01002b87
[!*] BreakPoint @ 01007147
[i] FS:[0] (TIB) is @ 0x7FFDF000
[i] Exception Handler [1] is handled by 0x7E440457 0x0007FE08
[i] Exception Handler [2] is handled by 0x7E440457 0x0007FE5C
[i] Exception Handler [3] is handled by 0x7E440457 0x0007FFB0
[i] Exception Handler [4] is handled by 0x010075BA 0x0007FFE0
[i] Exception Handler [5] is handled by 0x7C839AA8 0xFFFFFFFF
[i] Unsetting breakpoint @ 0x01007147

So that's it, short and sweet. The license is public domain, use as you see fit.

Thursday, 9 February 2012

A Glimpse of Our Next Product - A New Approach to Forensics

We thought we'd put out a little teaser to show you what we've been working on.

Our goal is to allow forensic investigators to work with the data rather than spend time learning the tool.



If you want to be at the front of the queue or are interested in more information feel free to get in contact with us.

Monday, 6 February 2012

Oracle Apex Application Security - A Year in Review

Introduction
We've been shipping our Oracle Application Express static code analysis product ApexSec for a year now. During the course of the year we've personally analysed over 60 different applications. For a new product and a new company gaining market traction in a niche aspect of security this isn't bad.

The applications we have seen have ranged in size from the small, at sub 10 pages, through to the large at over 200 pages. The average size across all the applications has been 21 pages. As we've done these assessments we've collected anonymous statistics on the volume of findings by type versus the size of the application in terms of pages.

Today we will provide some insight into the state of security in Oracle Application Express developed applications based on our dataset.

What ApexSec Checks for:
The ApexSec security checks can be broadly broken down into five categories:

  • Access control: A set of Apex specific access control constraints to pages, form, fields and processes.
  • Configuration: Apex specific configuration options that enhance security of the application.
  • SQL Injection: An input validation issue which can lead to unauthorised arbitrary database access.
  • Cross Site Scripting:  An input validation issue which can lead to a users session or the application becoming compromised.
  • Application logic: Variety of different vulnerabilities, some specific to Apex, others generic to the web, that manifest themselves through insecure development practices.

To provide some context in the larger sphere of web application security we're going to focus on two of these areas specifically; SQL Injection and Cross Site Scripting. Both are very common types of web security vulnerability and not specific to Oracle Application Express.

The Big Picture
At the forty thousand foot view the total percentage of applications indiscriminate of size or complexity that we've analysed that are vulnerable to these classes is as follows.

Cross Site Scripting
For Cross Site Scripting, a class of vulnerability which involves being able to inject JavaScript (client side code) into an application either in a reflective or persistent manner:


SQL Injection
For SQL Injection, where an external individual is able to manipulate database queries to either read or write in an unauthorized manner:


Considering our source sample set this is concerning as nearly 50% were less than 10 pages in size yet over 80% were vulnerable to Cross Site Scripting and 40% vulnerable to SQL injection. 

The reasons for the high proportion of small applications is due to our cloud service providing a free report for those with 15 pages or less combined with the fact our larger clients have bought copies to run in-house as part of their standard development processes, which we don't get to see any results for. 

Another skew on the data is that older applications that have been developed on Apex 3 and lower and then moved onto Apex 4 are not protected by the increased security defaults that exist in Apex 4 and above.


Applications with 20 pages or more
If we then start focussing our attention on larger and more complex applications these percentages quickly increase. The first cut-off point we used were 20 pages or more. This becomes more representative at the low end of applications of at a moderate level of complexity, pages and business logic typically serving a single purpose.

The percentage vulnerable to Cross Site Scripting becomes 100% based on our sample set.

For SQL Injection we see the percentage increases to 73%. This can be explained due to the increase in application logic coupled with the interaction between queries and a variety of different pages.

Looking at the average distinct instances of SQL Injection and Cross Site Scripting per application of those with 20 pages or more that are vulnerable we see the following.


This serves as an indication that these vulnerabilities typically increase not just in their presence but actual prevalence in terms of distinct instances as complexity increases.

Applications with 40 pages or more
When looking at applications with 40 pages or more we're typically into the territory of enterprise applications. Looking at the percentage of total applications that are vulnerable to our two classes of issue we see the following breakdown.

Again 100% for Cross Site Scripting:


An overall increase in the presence of SQL injection.


Again if we look at the average number of instances of these specific vulnerabilities across the vulnerable application we see an increase compared to smaller applications.


So we see that on average each vulnerable application of 40 or more pages has 19 separate instances of SQL Injection and 119 of Cross Site Scripting.

Conclusions
In conclusion it's clear from at least the Oracle Application Express applications we have personally looked at that with an increase in the number of pages and associated business logic comes the greater risk of being vulnerable.

The vulnerabilities found here are the same family of vulnerabilities found in most, if not all other web frameworks and should not be taken as a feature specific to Apex development. Code fixes are usually fairly trivial, a majority of the cost associated with fixing the vulnerabilities is searching for the code which is at fault.

So what are the next steps? If you would to see if your application is vulnerable you can upload your application at no cost to our cloud offering, in return you'll receive a summary report for free with an option to buy the full report if you'd like the technical details. Optionally if your application can not leave your organization you can purchase ApexSec to run locally or alternatively engage us as consultants to analyse your code and work with your developers.

Working with C++ DLL Exports without Source or Headers

We had a small problem that we had to overcome last week during an assessment. This blog entry is going to show the mistakes we made along the way and how it was finally solved.

We had acquired a 64 bit DLL which exported a C++ class and associated methods that contained some secret sauce (reversing was infeasible in the allotted time). We've put together the following example DLL to demonstrate:

#define DLLCPP_API __declspec(dllexport)  
  class DLLCPP_API CDLLCPP {  
  private:  
       bool bInit;  
  public:  
       CDLLCPP();  
       ~CDLLCPP();  
       int SuperSecretSauce(char *strString);  
  };  

As we didn't have the code, library or headers we ran dumpbin.exe /exports against the compiled DLL to reveal the exported names (note: the code project solution to dynamic C++ DLL loading needs headers):
 Microsoft (R) COFF/PE Dumper Version 10.00.40219.01  
 Copyright (C) Microsoft Corporation. All rights reserved.  
 Dump of file DLLCPP.dll  
 File Type: DLL  
  Section contains the following exports for DLLCPP.dll  
   00000000 characteristics  
   4F2E3F6C time date stamp Sun Feb 05 08:35:56 2012  
     0.00 version  
       1 ordinal base  
       4 number of functions  
       4 number of names  
   ordinal hint RVA   name  
      1  0 00001010 ??0CDLLCPP@@QEAA@XZ  
      2  1 00001020 ??1CDLLCPP@@QEAA@XZ  
      3  2 00001000 ??4CDLLCPP@@QEAAAEAV0@AEBV0@@Z  
      4  3 00001030 ?SuperSecretSauce@CDLLCPP@@QEAAHPEAD@Z  
  Summary  
     1000 .data  
     1000 .pdata  
     1000 .rdata  
     1000 .reloc  
     1000 .rsrc  
     1000 .text  
We wanted to call the SuperSecretSauce method, so ran undname.exe on the export (note: the DLL is 64bit in this example as well hence the __ptr64):
 C:\>undname ?SuperSecretSauce@CDLLCPP@@QEAAHPEAD@Z  
 Microsoft (R) C++ Name Undecorator  
 Copyright (C) Microsoft Corporation. All rights reserved.  
 Undecoration of :- "?SuperSecretSauce@CDLLCPP@@QEAAHPEAD@Z"  
 is :- "public: int __cdecl CDLLCPP::SuperSecretSauce(char * __ptr64) __ptr64"  
Pro Tip: You can run undname.exe against the output of dumpbin.exe /exports:
  • dumpbin /exports DLLCPP.dll > out.txt
  • undname < out.txt
We end  up with (our comments are in blue text):
   // The constructor  
   1  0 00001010 public: __cdecl CDLLCPP::CDLLCPP(void) __ptr64  
   // The deconstructor  
   2  1 00001020 public: __cdecl CDLLCPP::~CDLLCPP(void) __ptr64  
   // The class  
   3  2 00001000 public: class CDLLCPP & __ptr64 __cdecl CDLLCPP::operator=(class CDLLCPP const & __ptr64) __ptr64  
   // The SuperSecretSauce Method  
   4  3 00001030 public: int __cdecl CDLLCPP::SuperSecretSauce(char * __ptr64) __ptr64  
Pro Tip 2: There is also the the UnDecorateSymbolName function in DbgHelp

We put together some code to try and call the method like so:

#include "stdafx.h"  
  #include <Windows.h>  
  #include <strsafe.h>  
  #include <Dbghelp.h>  
  typedef void (__cdecl* _ExampleSecretSauce)(char *);  
  int _tmain(int argc, _TCHAR* argv[])  
  {  
       fprintf(stdout,"[i] Recx - C++ class DLL example\n");  
       HMODULE modDLL = LoadLibrary(L".\\DLLCPP.dll");  
       if (modDLL==NULL)  
       {  
            fprintf(stderr,"[!] Could not load .\\DLLCPP.dll");            
            return 1;  
       }  
       _ExampleSecretSauce OurImportedSecretSauce  = (_ExampleSecretSauce)GetProcAddress(modDLL,"?SuperSecretSauce@CDLLCPP@@QEAAHPEAD@Z");  
       if(OurImportedSecretSauce==NULL){  
            fprintf(stderr,"[!] Could not get address of the secret sauce method");            
            return 1;  
       }  
       OurImportedSecretSauce("Hello Secret Sauce");  
       return 0;  
  }  
But when we run this code we get the following response from the SuperSecretSauce method if were lucky enough that it doesn't crash:
[i] Recx - C++ class DLL example
[!] Our constructor has not been called!
The error above is due a check we've put in the demo method, in the real world all manner of unpredictable outcomes could occur. At which point its worth pointing out when you normally instantiate a method from a C++ class the code would be something like this:
#include "stdafx.h"  
  #include "DLLCPP.h"  
  #pragma comment(lib,".\\Release\\dllcpp.lib")  
  int _tmain(int argc, _TCHAR* argv[])  
  {  
       CDLLCPP *cppDLL = new CDLLCPP();  
       cppDLL->SuperSecretSauce("Hello");  
       return 0;  
  }  
So we modified the code to first import and call the constructor.
#include "stdafx.h"  
  #include <Windows.h>  
  #include <strsafe.h>  
  #include <Dbghelp.h>  
  typedef void (__cdecl* _ExampleConstruct)();  
  typedef void (__cdecl* _ExampleSecretSauce)(char *);  
  int _tmain(int argc, _TCHAR* argv[])  
  {  
       fprintf(stdout,"[i] Recx - C++ class DLL example\n");  
       HMODULE modDLL = LoadLibrary(L".\\DLLCPP.dll");  
       if (modDLL==NULL)  
       {  
            fprintf(stderr,"[!] Could not load .\\DLLCPP.dll");            
            return 1;  
       }  
       _ExampleConstruct OurImportedConstructor = (_ExampleConstruct)GetProcAddress(modDLL,"??0CDLLCPP@@QEAA@XZ");  
       if(OurImportedConstructor==NULL){  
            fprintf(stderr,"[!] Could not get address of the constructor");            
            return 1;  
       }  
       _ExampleSecretSauce OurImportedSecretSauce  = (_ExampleSecretSauce)GetProcAddress(modDLL,"?SuperSecretSauce@CDLLCPP@@QEAAHPEAD@Z");  
       if(OurImportedSecretSauce==NULL){  
            fprintf(stderr,"[!] Could not get address of the secret sauce method");            
            return 1;  
       }  
       OurImportedConstructor();  
       OurImportedSecretSauce("Hello Secret Sauce");  
       return 0;  
  }  
When we ran this code it just crashed with an access violation in the constructor . This should have been obvious in hindsight. If we look at the disassembly for a legitimate call on 64 bit to new() and the constructor in the demo above we see the following (our comments in blue text):
  000000013FF71000 sub   rsp,38h    
  000000013FF71004 mov   qword ptr [rsp+20h],0FFFFFFFFFFFFFFFEh    
    CDLLCPP *cppDLL = new CDLLCPP();   
  000000013FF7100D mov   ecx,1  
  000000013FF71012 call  qword ptr [__imp_operator new (13FF72148h)]    
  // Microsoft x64 calling convention says the ret comes back in RAX (pointer)  
  000000013FF71018 mov   qword ptr [rsp+50h],rax    
  000000013FF7101D test  rax,rax    
  000000013FF71020 je   wmain+2Ch (13FF7102Ch)    
  // The first argument - the pointer to the object - is passed in RCX - to the constructor  
  000000013FF71022 mov   rcx,rax    
  000000013FF71025 call  qword ptr [__imp_CDLLCPP::CDLLCPP (13FF72008h)]    
  000000013FF7102B nop    
    cppDLL->SuperSecretSauce("Hello");   
  000000013FF7102C lea   rdx,[string "Hello" (13FF721D0h)]    
  // Again passing a pointer to the object in RCX from the original RAX result from new  
  000000013FF71033 mov   rcx,rax    
  000000013FF71036 call  qword ptr [__imp_CDLLCPP::SuperSecretSauce (13FF72000h)]   
Also there was a likely hint in the dumpbin out (in red text):
   // The constructor  
   1  0 00001010 public: __cdecl CDLLCPP::CDLLCPP(void) __ptr64  
   // The deconstructor  
   2  1 00001020 public: __cdecl CDLLCPP::~CDLLCPP(void) __ptr64  
   // The class  
   3  2 00001000 public: class CDLLCPP & __ptr64 __cdecl CDLLCPP::operator=(class CDLLCPP const & __ptr64) __ptr64  
   // The SuperSecretSauce Method  
   4  3 00001030 public: int __cdecl CDLLCPP::SuperSecretSauce(char * __ptr64) __ptr64  
All of the exports, after the function definition, listed an extra 64bit pointer. While we don't know for sure (as the output of undname isn't fully documented), but we think this is likely an indicator of the need to pass the object pointer. So the reason for the crash was that there was no pointer to an object being passed to the constructor or the SuperSecretSauce method. The way we solved this problem was to supply an extra parameter at the start to the function typedef for any method in that class. This would ensure that the pointer to our object is passed in the RCX register (note: there is only one calling convention on Windows 64bit):
typedef void (__cdecl* _ExampleConstruct)(char *);  
  typedef void (__cdecl* _ExampleSecretSauce)(char *, char *);  
We then allocate a block of memory big enough to hold it (an exercise for the reader to work out how large it needs to be), memset and pass it to the constructor and other methods.
char vFakeObject[4096];  
      memset(vFakeObject,0x00,4096);  
      OurImportedConstructor(vFakeObject);  
      OurImportedSecretSauce(vFakeObject,"Hello Secret Sauce");  
So we end up with code that looks like this:
#include "stdafx.h"  
  #include <Windows.h>  
  #include <strsafe.h>  
  #include <Dbghelp.h>  
  typedef void (__cdecl* _ExampleConstruct)(char *);  
  typedef void (__cdecl* _ExampleSecretSauce)(char *, char *);  
  int _tmain(int argc, _TCHAR* argv[])  
  {  
       fprintf(stdout,"[i] Recx - C++ class DLL example\n");  
       HMODULE modDLL = LoadLibrary(L".\\DLLCPP.dll");  
       if (modDLL==NULL)  
       {  
            fprintf(stderr,"[!] Could not load .\\DLLCPP.dll");            
            return 1;  
       }  
       _ExampleConstruct OurImportedConstructor = (_ExampleConstruct)GetProcAddress(modDLL,"??0CDLLCPP@@QEAA@XZ");  
       if(OurImportedConstructor==NULL){  
            fprintf(stderr,"[!] Could not get address of the constructor");            
            return 1;  
       }  
       _ExampleSecretSauce OurImportedSecretSauce  = (_ExampleSecretSauce)GetProcAddress(modDLL,"?SuperSecretSauce@CDLLCPP@@QEAAHPEAD@Z");  
       if(OurImportedSecretSauce==NULL){  
            fprintf(stderr,"[!] Could not get address of the secret sauce method");            
            return 1;  
       }  
       char vFakeObject[4096];  
       memset(vFakeObject,0x00,4096);  
       OurImportedConstructor(vFakeObject);  
       OurImportedSecretSauce(vFakeObject,"Hello Secret Sauce");  
       return 0;  
  }  
And... drum role... voilĂ :
[i] Recx - C++ class DLL example
[!] You sent the secret sauce Hello Secret Sauce
Hopefully this will save you an hour or two of reading plus trial and error. All of the code for the above examples can be downloaded from here. The projects included in the download archive are:
  • DLLCPP - The C++ DLL.
  • LoaderAttempt1 - the example where we don't call the constructor.
  • LoaderAttempt2 - the example where we don't pass an object.
  • Loader - the working example.
The project as-is will only work for the 64bit build as the undecorated function names will need changing for the 32bit version of the DLL.