Copyright © Microsoft Corporation. This document is an archived reproduction of a version originally published by Microsoft. It may have slight formatting modifications for consistency and to improve readability.
Figure 1   PEDIFF.CPP


//==========================================
// Matt Pietrek, Microsoft Systems Journal, November 1997
// FILE: PEDIFF.CPP
//==========================================
#include <windows.h>
#include <imagehlp.h>
#include <stdio.h>
#include <malloc.h>
#include "PEExportList.h"
//=============================== Global Variables ============================
char g_szHelpText[] = 
"Syntax: PEDIFF file1 [file2]\n\n" 
"If only one file is specified, it is assumed to be a comparison file to a \n"
"similarly named file in the current directory.\n\n"
"For example: PEDUMP C:\\WINNT\\SYSTEM32\\KERNEL32.DLL will compare that file\n"
"to the KERNEL32.DLL in the current directory.\n";
PSTR pszFile1 = 0;
PSTR pszFile2 = 0;
//==================================== Code ===================================
#define MY_PROCESS_HANDLE 0
BOOL LoadExportInfo( char * pszFilename, PEExportList * pExportList )
{
    LOADED_IMAGE li;
    if ( !MapAndLoad( pszFilename, "", &li, TRUE, TRUE ) )
    {
        printf( "Unable to locate or load %s\n", pszFilename );
        return FALSE;
    }
    PIMAGE_EXPORT_DIRECTORY pExpDir;
    pExpDir = (PIMAGE_EXPORT_DIRECTORY)(li.FileHeader->OptionalHeader.
                DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);
    if ( !pExpDir )
    {
        printf( "No Exports\n" );
        return FALSE;
    }
    pExpDir = (PIMAGE_EXPORT_DIRECTORY)ImageRvaToVa(
                    li.FileHeader, li.MappedAddress, (DWORD)pExpDir, 0 );
    PDWORD * pExpNames = pExpDir->AddressOfNames;
    pExpNames = (PDWORD *)ImageRvaToVa(
                    li.FileHeader, li.MappedAddress, (DWORD)pExpNames, 0 );
    for ( unsigned i=0; i < pExpDir->NumberOfNames; i++ )
    {
        PSTR p = (PSTR)ImageRvaToVa(
                    li.FileHeader, li.MappedAddress, (DWORD)*pExpNames, 0 );
        pExportList->AddSymbol( p );
        pExpNames++;
    }
    // In case the DLL exports any APIs by ordinal only, we need to
    // synthesize a name (e.g., "ordinal_102", and add it to the list
    if ( pExpDir->NumberOfNames < pExpDir->NumberOfFunctions )
    {
        // First, get a pointer to the AddressOfNameOrdinals array
        PWORD pNameOrdinals = (PWORD)ImageRvaToVa(
                        li.FileHeader, li.MappedAddress,
                        (DWORD)pExpDir->AddressOfNameOrdinals, 0 );
        // Next, get a pointer to the AddressOfFunctions array
        PDWORD pFunctions = (PDWORD)ImageRvaToVa(
                        li.FileHeader, li.MappedAddress,
                        (DWORD)pExpDir->AddressOfFunctions, 0 );
        DWORD cNamedOrdinals = pExpDir->NumberOfNames;
        DWORD cFunctions = pExpDir->NumberOfFunctions;
        // Allocate memory for an array of BYTEs, each BYTE indicating if
        // a function is exported by name.  Initially, all entries are set to
        // 0, meaning they're exported by ordinal
        PBYTE pNamedFuncs = (PBYTE)calloc( cFunctions, sizeof(BYTE) );
        // Iterate through the NamedOrdinals array, and for each ordinal,
        // set the corresponding entry in the pNamedFunctions array to 1
        // (meaning "exported by name"
        PWORD pCurrOrd = pNameOrdinals;
        for ( unsigned i = 0; i < cNamedOrdinals; i++, pCurrOrd++ )
            pNamedFuncs[ *pCurrOrd ] = 1;
        // Finally, iterate through the pNamedFunctions array.  For each
        // function, check if it's named, or if its address is 0.  If neither
        // is true, the function is exported by ordinal only, so synthesize
        // a name and add it to the PEExportList
        for ( i = 0; i < cFunctions; i++ )
        {
            if ( pNamedFuncs[i] )   // If marked, it's a named export
                continue;
            if ( 0 == pFunctions[i] )   // If the address is 0, it's not
                continue;               // a blank (non-exported) function
            char szMadeUpName[64];
            wsprintf( szMadeUpName, "ordinal_%u", pExpDir->Base + i );
            pExportList->AddSymbol( szMadeUpName );
        }
        free( pNamedFuncs );    // Free our allocated buffer
    }
    UnMapAndLoad( &li );  // Close the PE file and debug info mapping
    return TRUE;
}
BOOL ProcessCommandLine( int argc, char *argv[] )
{
    if ( (argc < 2) || (argc > 3) )
        return FALSE;
    pszFile1 = argv[1];         // First real argument is first filename
    if ( 3 == argc )            // If 2 real arguments, second arg is the
    {                           // the second filename
        pszFile2 = argv[2];
        return TRUE;        
    }
    // If we get here, only one filename was specified.  It should contain
    // a path.  Therefore, we should be able to point pszFile2 to the
    // character after the final blackslash, and be done with it.
    pszFile2 = strrchr( pszFile1, '\\' );
    if ( !pszFile2 )        // Hmmm... No blackslash.. Something's not right.
        return FALSE;
    pszFile2++;             // Increment past the backslash to the filename
    return TRUE;
}
int main( int argc, char * argv[] )
{   
    if ( !ProcessCommandLine( argc, argv ) )
    {
        printf( g_szHelpText );
        return 0;
    }
    PEExportList file1;
    if ( !LoadExportInfo( pszFile1, &file1 ) )
        return 1;
    PEExportList file2;
    if ( !LoadExportInfo( pszFile2, &file2 ) )
        return 1;
    ExportedSymbolInfo * pCurrent = 0;
    while ( pCurrent = file1.GetNextSymbol(pCurrent) )
    {
        ExportedSymbolInfo * pMatch = file2.LookupSymbol( pCurrent->m_pszName );
        if ( pMatch )
        {
            pCurrent->m_flags |= EXPSYMINFO_MATCH;
            pMatch->m_flags   |= EXPSYMINFO_MATCH;
        }       
    }
    printf( "File: %s\n", pszFile1 );
    pCurrent = 0;
    while ( pCurrent = file1.GetNextSymbol(pCurrent) )
    {
        if ( 0 == pCurrent->m_flags & EXPSYMINFO_MATCH )
        {
            printf( "  %s\n", pCurrent->m_pszName );
        }       
    }
    printf( "File: %s\n", pszFile2 );
    pCurrent = 0;
    
    while ( pCurrent = file2.GetNextSymbol(pCurrent) )
    {
        if ( 0 == pCurrent->m_flags & EXPSYMINFO_MATCH )
        {
            printf( "  %s\n", pCurrent->m_pszName );
        }       
    }
    return 0;
}

Figure 2   PEExportList

PEExportList.H


//==========================================
// Matt Pietrek, Microsoft Systems Journal, November 1997
// FILE: PEExportList.H
//==========================================
#ifndef __PEEXPORTLIST_H
#define __PEEXPORTLIST_H
typedef enum ExportedSymbolInfoFlags
{
    EXPSYMINFO_MATCH = 1
};
struct ExportedSymbolInfo
{
    char *      m_pszName;
    unsigned    m_flags;
};
const int PEExportListGrowSize = 100;
class PEExportList
{
    ExportedSymbolInfo *    m_pSymbols;
    unsigned                m_symbolCount;
    public:
    PEExportList( void );
    ~PEExportList( void );
    ExportedSymbolInfo * AddSymbol( char * pszSymbolName );
    ExportedSymbolInfo * LookupSymbol( char * pszSymbolName );
    ExportedSymbolInfo * GetNextSymbol( ExportedSymbolInfo * );
};
#endif
PEExportList.CPP

//==========================================
// Matt Pietrek, Microsoft Systems Journal, November 1997
// FILE: ExportList.CPP
//==========================================
#include <windows.h>
#include <string.h>
#include <malloc.h>
#include "PEExportList.h"
PEExportList::PEExportList( void )
{
    m_pSymbols = 0;
    m_symbolCount = 0;
}
PEExportList::~PEExportList( void )
{
    ExportedSymbolInfo * pCurrent = m_pSymbols;
    
    for ( unsigned i = 0; i < m_symbolCount; i++, pCurrent++ )
    {
        free( pCurrent->m_pszName );    
    }
}
ExportedSymbolInfo *
PEExportList::AddSymbol( char * pszSymbolName )
{
    if ( 0 == (m_symbolCount % PEExportListGrowSize) )
    {
        m_pSymbols = (ExportedSymbolInfo *)
            realloc(m_pSymbols,
                    (m_symbolCount + PEExportListGrowSize)
                    * sizeof(ExportedSymbolInfo) );
    }
    ExportedSymbolInfo * pCurrent = &m_pSymbols[m_symbolCount];
    pCurrent->m_pszName = strdup( pszSymbolName );
    pCurrent->m_flags = 0;
    m_symbolCount++;
    return pCurrent;        
}
ExportedSymbolInfo *
PEExportList::LookupSymbol( char * pszSymbolName )
{
    ExportedSymbolInfo * pSymbol = 0;
    while ( pSymbol = GetNextSymbol( pSymbol ) )
    {
        if ( 0 == strcmp(pSymbol->m_pszName, pszSymbolName) )
            break;
    }
    return pSymbol;
}
ExportedSymbolInfo *
PEExportList::GetNextSymbol( ExportedSymbolInfo * pSymbol )
{
    ExportedSymbolInfo * pEndSymbol = &m_pSymbols[m_symbolCount];
    if ( 0 == pSymbol && m_symbolCount )
    {
        return m_pSymbols;
    }
    else
    {
        pSymbol++;
        if ( pSymbol < pEndSymbol )
            return pSymbol;
        else
            return 0;
    }
}


Figure 3   Windows NT 4.0 and 5.0 API Differences


 File: c:\WINNT\system32\user32.dll
 FullScreenControl
 
 File: e:\winnt\system32\user32.dll
 AlignRects
 AnimateWindow
 BlockInput
 CreateDesktopExA
 CreateDesktopExW
 CreateTerminal
 EnumDisplayMonitors
 EnumDisplaySettingsExA
 EnumDisplaySettingsExW
 GetAltTabInfoA
 GetAltTabInfoW
 GetAncestor
 GetClipboardSequenceNumber
 GetComboBoxInfo
 GetCursorFrameInfo
 GetGUIThreadInfo
 GetGuiResources
 GetListBoxInfo
 GetMenuBarInfo
 GetMenuInfo
 GetMonitorInfoA
 GetMonitorInfoW
 GetMouseMovePoints
 GetScrollBarInfo
 GetTitleBarInfo
 GetWindowInfo
 GetWindowModuleFileNameA
 GetWindowModuleFileNameW
 IMPGetIMEA
 IMPGetIMEW
 IMPQueryIMEA
 IMPQueryIMEW
 IMPSetIMEA
 IMPSetIMEW
 InSendMessageEx
 InitializeWin32EntryTable
 InstallDeviceWorker
 MonitorFromPoint
 MonitorFromRect
 MonitorFromWindow
 NotifyWinEvent
 OpenInputDesktopEx
 PrivateSetDbgTag
 PrivateSetRipFlags
 RealChildWindowFromPoint
 RegisterDeviceNotificationA
 RegisterDeviceNotificationW
 ResolveDesktopForWOW
 SendIMEMessageExA
 SendIMEMessageExW
 SendInput
 SetMenuInfo
 SetWinEventHook
 UnhookWinEvent
 UnregisterDeviceNotification
 User32InitializeImmEntryTable
 WINNLSEnableIME
 WINNLSGetEnableStatus
 WINNLSGetIMEHotkey