Go4Expert

Go4Expert (http://www.go4expert.com/)
-   C++ (http://www.go4expert.com/articles/cpp-tutorials/)
-   -   Dll Wrapper: Making our own psapi.dll with .DEF (http://www.go4expert.com/articles/dll-wrapper-psapidll-def-t29602/)

david_BS 9Apr2013 20:32

Dll Wrapper: Making our own psapi.dll with .DEF
 
1 Attachment(s)
Level: Beginner, Test environment: WinXP SP3

This tutorial is going to teach you how to make a 'DLL wrapper'. Wrappers are own versions of known DLL's. In this case we're going to make a wrapper of a very known DLL known as PSAPI.DLL. If you don't know it then find the information somewhere XD. But it is about a DLL that is very common to find it loaded in some processes.

The technique of building wrappers has at least 2 objectives:
  1. Hooking: Because our own version of the DLL is going to contain our own versions of the original functions, and at the same time we need to call the originals within ours. What we can do is to execute our code before it gets executed the original code.
  2. Loading a DLL: What we are doing when we create our own version of the known DLL is making the target program to be loaded our DLL in place of the original DLL. This loading it's not made by the target program but by the operating system. The OS detects our DLL in the same location than the executable file of the program and it loads the DLL automatically, since there is dependency with the DLL.
In what fits with security, this technique is not really effective to make a hack for a program which has some kind of security. It would be so easy to validate files on disk, meaning the files that are needed by the program. The validation can be made using MD5 for example.

A file path validation could be also implemented, etc etc but if all this is not for hacking, then it is an attractive idea, at least for educational purposes.

Now about how to create something like this.. well, the first step is to know the original DLL that we wanna replace, meaning all its functions or at least the ones who are used by the target process (dependencies).

This means that we ought to know these functions prototypes, including their calling convention and some. Having that in the first place, we're going to get in mind that we also need to use the original DLL, because we depend on the original functions. This is solved by changing the name of the original DLL. In the case of PSAPI.DLL we can change the name to PSAPI2.DLL.

Then we're going to need to load the original renamed DLL and to make a dynamic linking to it, and to get the addresses of the original functions that we are needing. Obviously we do this with LoadLibrary + GetProcAddress.

After that, in our own DLL we place our hooking functions respecting the prototypes and we place the return addresses of the original functions that we dynamically imported with GetProcAddress.

We obviously use pointers to function for the returns in the hooking functions.

The calling convention used in PSAPI.DLL para its functions is __stdcall, for that, we can make that all functions in our project would be __stdcall by default, so won't need to specify it explicitly in each one of them.

http://imgs.g4estatic.com/dll-wrapper/wrapper4.png

Let's see PSAPI.DLL

http://imgs.g4estatic.com/dll-wrapper/wrapper1w.png

Let's take a look at its exports

http://imgs.g4estatic.com/dll-wrapper/wrapper2y.png

Let's concentrate in one of its functions for now, as an example EnumProcesses

http://imgs.g4estatic.com/dll-wrapper/wrapper3p.png

We need a function prototype, let's define a datatype corresponding to this function
Code:

typedef BOOL (* EnumProcesses_t)(DWORD *,DWORD,DWORD *);
We create a pointer to functions that is going to work as the return to the original function inside the hooking function.
Code:

BOOL (* pEnumProcesses)(DWORD *,DWORD,DWORD *);
Create the hooking functions (See the return is with the pointer to the original)
Code:

BOOL  newEnumProcesses(DWORD *pProcessIds, DWORD cb, DWORD *pBytesReturned)
{
    return pEnumProcesses(pProcessIds, cb, pBytesReturned);
}

Now we create another instance (not really necessary in this case), this
function will be an exported one so it can be used by the target program.
Code:

BOOL  _EnumProcesses(DWORD *pProcessIds, DWORD cb, DWORD *pBytesReturned)
{
    return newEnumProcesses(pProcessIds, cb, pBytesReturned);
}

And basically we do the same with the other functions XD. To compile this project it is required the file psapi.h wich contains the data types used in the hooking functions. it is not requered psapi.lib that is a static linking library.

We are going to need a .DEF file that has to be included to the project. There are rules to implement this .DEF file and they are these:
  1. It has to start with saying LIBRARY and the name of the DLL
  2. After that, it has to say EXPORTS
  3. It has to be placed one by one, the names that we desire for each symbol, following that, an equal sign '=' and the real name of the symbol, which is gotten from the disassembly.
  4. After that, it is left a whitespace and it is placed an 'arroba' and the index number corresponding to the export.
But, let's see an image for better understanding:

http://imgs.g4estatic.com/dll-wrapper/wrapper5.png

Let's see the DLL code:

In main.cpp there are the function prototypes and the pointers to function.
We will look at the example of EnumProcesses
Code:

typedef BOOL (* EnumProcesses_t)(DWORD *,DWORD,DWORD *);
Code:

BOOL (* pEnumProcesses)(DWORD *,DWORD,DWORD *);
then we have the hooking functions. These return a pointer to function.
Code:

BOOL  newEnumProcesses(DWORD* pProcessIds, DWORD cb, DWORD* pBytesReturned)
{
    return pEnumProcesses(pProcessIds, cb, pBytesReturned);
}

Then it is shown a code which is about a class and the creation of an object of the type of the class, This makes possible the initialization of the function pointers when the program is executed, more precisely when the object is created.
Code:

/*
class Inicializar
{
public:
    Inicializar()
    {
        pEnumProcesses = 0;
        pEnumProcessModules = 0;
        pGetModuleBaseNameA = 0;
        pGetModuleBaseNameW = 0;
        pGetModuleFileNameExA = 0;
        pGetModuleFileNameExW = 0;
        pGetModuleInformation = 0;
        pEmptyWorkingSet = 0;
        pQueryWorkingSet = 0;
        pInitializeProcessForWsWatch = 0;   
        pGetWsChanges = 0;
        pGetMappedFileNameW = 0;
        pGetMappedFileNameA = 0;
        pEnumDeviceDrivers = 0;
        pGetDeviceDriverBaseNameA = 0;
        pGetDeviceDriverBaseNameW = 0;
        pGetDeviceDriverFileNameA = 0;
        pGetDeviceDriverFileNameW = 0;
        pGetProcessMemoryInfo = 0;
        pEnumPageFilesA =0;
        pEnumPageFilesW =0;
        pGetPerformanceInfo =0;
        pGetProcessImageFileNameA =0;
        pGetProcessImageFileNameW=0;

        HINSTANCE myDLL;
        myDLL = LoadLibrary("psapi2.dll");

        if(myDLL != NULL)
        {
          pEnumProcesses = (EnumProcesses_t)GetProcAddress(myDLL, "EnumProcesses");
          pEnumProcessModules = (EnumProcessModules_t)GetProcAddress(myDLL, "EnumProcessModules");
          pGetModuleBaseNameA = (GetModuleBaseNameA_t)GetProcAddress(myDLL, "GetModuleBaseNameA");
          pGetModuleBaseNameW = (GetModuleBaseNameW_t)GetProcAddress(myDLL, "GetModuleBaseNameW");
          pGetModuleFileNameExA = (GetModuleFileNameExA_t)GetProcAddress(myDLL, "GetModuleFileNameExA");
          pGetModuleFileNameExW = (GetModuleFileNameExW_t)GetProcAddress(myDLL, "GetModuleFileNameExW");
          pGetModuleInformation = (GetModuleInformation_t)GetProcAddress(myDLL, "GetModuleInformation");
          pEmptyWorkingSet = (EmptyWorkingSet_t)GetProcAddress(myDLL, "EmptyWorkingSet");
          pQueryWorkingSet = (QueryWorkingSet_t)GetProcAddress(myDLL, "QueryWorkingSet");
          pInitializeProcessForWsWatch = (InitializeProcessForWsWatch_t)GetProcAddress(myDLL, "InitializeProcessForWsWatch");       
          pGetWsChanges = (GetWsChanges_t)GetProcAddress(myDLL, "GetWsChanges");
          pGetMappedFileNameW = (GetMappedFileNameW_t)GetProcAddress(myDLL, "GetMappedFileNameW");
          pGetMappedFileNameA = (GetMappedFileNameA_t)GetProcAddress(myDLL, "GetMappedFileNameA");
          pEnumDeviceDrivers = (EnumDeviceDrivers_t)GetProcAddress(myDLL, "EnumDeviceDrivers");
          pGetDeviceDriverBaseNameA = (GetDeviceDriverBaseNameA_t)GetProcAddress(myDLL, "GetDeviceDriverBaseNameA");
          pGetDeviceDriverBaseNameW = (GetDeviceDriverBaseNameW_t)GetProcAddress(myDLL, "GetDeviceDriverBaseNameW");
          pGetDeviceDriverFileNameA = (GetDeviceDriverFileNameA_t)GetProcAddress(myDLL, "GetDeviceDriverFileNameA");
          pGetDeviceDriverFileNameW = (GetDeviceDriverFileNameW_t)GetProcAddress(myDLL, "GetDeviceDriverFileNameW");
          pGetProcessMemoryInfo = (GetProcessMemoryInfo_t)GetProcAddress(myDLL, "GetProcessMemoryInfo");
          pEnumPageFilesA =(EnumPageFilesA_t)GetProcAddress(myDLL, "EnumPageFilesA");
          pEnumPageFilesW =(EnumPageFilesW_t)GetProcAddress(myDLL, "EnumPageFilesW");
          pGetPerformanceInfo =(GetPerformanceInfo_t)GetProcAddress(myDLL, "GetPerformanceInfo");
          pGetProcessImageFileNameA =(GetProcessImageFileNameA_t)GetProcAddress(myDLL, "GetProcessImageFileNameA");
          pGetProcessImageFileNameW =(GetProcessImageFileNameW_t)GetProcAddress(myDLL, "GetProcessImageFileNameW");
        }
    }
};


//
Inicializar Init;
*/

That is commented because it is done already in the 'DllMain' function,
Code:

BOOL APIENTRY DllMain( HANDLE hModule, DWORD  ul_reason_for_call, LPVOID lpReserved )
{
    switch (ul_reason_for_call)
    {
        case DLL_PROCESS_ATTACH:
            {
                pEnumProcesses = 0;
                pEnumProcessModules = 0;
                pGetModuleBaseNameA = 0;
                pGetModuleBaseNameW = 0;
                pGetModuleFileNameExA = 0;
                pGetModuleFileNameExW = 0;
                pGetModuleInformation = 0;
                pEmptyWorkingSet = 0;
                pQueryWorkingSet = 0;
                pInitializeProcessForWsWatch = 0;   
                pGetWsChanges = 0;
                pGetMappedFileNameW = 0;
                pGetMappedFileNameA = 0;
                pEnumDeviceDrivers = 0;
                pGetDeviceDriverBaseNameA = 0;
                pGetDeviceDriverBaseNameW = 0;
                pGetDeviceDriverFileNameA = 0;
                pGetDeviceDriverFileNameW = 0;
                pGetProcessMemoryInfo = 0;
                pEnumPageFilesA =0;
                pEnumPageFilesW =0;
                pGetPerformanceInfo =0;
                pGetProcessImageFileNameA =0;
                pGetProcessImageFileNameW=0;

                HINSTANCE myDLL;
                myDLL = LoadLibrary("psapi2.dll");

                if(myDLL != NULL)
                {
                  pEnumProcesses = (EnumProcesses_t)GetProcAddress(myDLL, "EnumProcesses");
                  pEnumProcessModules = (EnumProcessModules_t)GetProcAddress(myDLL, "EnumProcessModules");
                  pGetModuleBaseNameA = (GetModuleBaseNameA_t)GetProcAddress(myDLL, "GetModuleBaseNameA");
                  pGetModuleBaseNameW = (GetModuleBaseNameW_t)GetProcAddress(myDLL, "GetModuleBaseNameW");
                  pGetModuleFileNameExA = (GetModuleFileNameExA_t)GetProcAddress(myDLL, "GetModuleFileNameExA");
                  pGetModuleFileNameExW = (GetModuleFileNameExW_t)GetProcAddress(myDLL, "GetModuleFileNameExW");
                  pGetModuleInformation = (GetModuleInformation_t)GetProcAddress(myDLL, "GetModuleInformation");
                  pEmptyWorkingSet = (EmptyWorkingSet_t)GetProcAddress(myDLL, "EmptyWorkingSet");
                  pQueryWorkingSet = (QueryWorkingSet_t)GetProcAddress(myDLL, "QueryWorkingSet");
                  pInitializeProcessForWsWatch = (InitializeProcessForWsWatch_t)GetProcAddress(myDLL, "InitializeProcessForWsWatch");       
                  pGetWsChanges = (GetWsChanges_t)GetProcAddress(myDLL, "GetWsChanges");
                  pGetMappedFileNameW = (GetMappedFileNameW_t)GetProcAddress(myDLL, "GetMappedFileNameW");
                  pGetMappedFileNameA = (GetMappedFileNameA_t)GetProcAddress(myDLL, "GetMappedFileNameA");
                  pEnumDeviceDrivers = (EnumDeviceDrivers_t)GetProcAddress(myDLL, "EnumDeviceDrivers");
                  pGetDeviceDriverBaseNameA = (GetDeviceDriverBaseNameA_t)GetProcAddress(myDLL, "GetDeviceDriverBaseNameA");
                  pGetDeviceDriverBaseNameW = (GetDeviceDriverBaseNameW_t)GetProcAddress(myDLL, "GetDeviceDriverBaseNameW");
                  pGetDeviceDriverFileNameA = (GetDeviceDriverFileNameA_t)GetProcAddress(myDLL, "GetDeviceDriverFileNameA");
                  pGetDeviceDriverFileNameW = (GetDeviceDriverFileNameW_t)GetProcAddress(myDLL, "GetDeviceDriverFileNameW");
                  pGetProcessMemoryInfo = (GetProcessMemoryInfo_t)GetProcAddress(myDLL, "GetProcessMemoryInfo");
                  pEnumPageFilesA =(EnumPageFilesA_t)GetProcAddress(myDLL, "EnumPageFilesA");
                  pEnumPageFilesW =(EnumPageFilesW_t)GetProcAddress(myDLL, "EnumPageFilesW");
                  pGetPerformanceInfo =(GetPerformanceInfo_t)GetProcAddress(myDLL, "GetPerformanceInfo");
                  pGetProcessImageFileNameA =(GetProcessImageFileNameA_t)GetProcAddress(myDLL, "GetProcessImageFileNameA");
                  pGetProcessImageFileNameW =(GetProcessImageFileNameW_t)GetProcAddress(myDLL, "GetProcessImageFileNameW");
               
                  DisableThreadLibraryCalls (myDLL);
                }

                if(!pEnumProcesses||!pEnumProcessModules ||!pGetModuleBaseNameA ||!pGetModuleBaseNameW
                    ||!pGetModuleFileNameExA ||!pGetModuleFileNameExW ||!pGetModuleInformation
                    ||!pEmptyWorkingSet ||!pQueryWorkingSet
                   
                    ||!pInitializeProcessForWsWatch ||!pGetWsChanges ||!pGetMappedFileNameW
                    ||!pGetMappedFileNameA
                    ||!pEnumDeviceDrivers ||!pGetDeviceDriverBaseNameA ||!pGetDeviceDriverBaseNameW
                    ||!pGetDeviceDriverFileNameA ||!pGetDeviceDriverFileNameW ||!pGetProcessMemoryInfo
                    ||!pEnumPageFilesA ||!pEnumPageFilesW
                    ||!pGetPerformanceInfo ||!pGetProcessImageFileNameA ||!pGetProcessImageFileNameW)
                {
                    printf("Wrapper not installed!\n");
                    system("pause");
                }
                else
                {
                    extern DWORD AddEnumProcesses;
                    printf("DLL\n");
                    printf("EnumProcesses 0x%x\n",AddEnumProcesses);
                    printf("newEnumProcesses 0x%x\n",newEnumProcesses);
                    printf("hModule 0x%x\n",hModule);
                    printf("PSAPI2.DLL 0x%x\n",GetModuleHandle("psapi2.dll"));
                    printf("pEnumProcesses 0x%x\n",pEnumProcesses);
                    system("pause");
                }
            }
            break;

        case DLL_THREAD_ATTACH:
        case DLL_THREAD_DETACH:
        case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}

///////////////////////////////

In exports.cpp there are only the exported functions, which are referenced in the .DEF file. These functions return to the hooking functions. This of having 2 instances is not really necesary as it was stated before, but it is if you wish to implement another method that I will expose later.
Code:

BOOL  _EnumProcesses(DWORD* pProcessIds, DWORD cb, DWORD* pBytesReturned)
{
    return newEnumProcesses(pProcessIds, cb, pBytesReturned);
}

Let's take a look at the 'Dummy' program that works as the target program.
The target program requieres psapi.lib to build it with.
Code:

//
// By 85
// elhacker.net
// etalking.com.ar
// 2013
//

#pragma comment(lib,"psapi.lib")
#include<windows.h>
#include<stdio.h>

//////////////////////

#define DLLIMPORTING extern "C" __declspec (dllimport)
DLLIMPORTING BOOL WINAPI EnumProcesses(DWORD *,DWORD,DWORD *);
DLLIMPORTING BOOL WINAPI EnumProcessModules(HANDLE,HMODULE *,DWORD,LPDWORD);
DLLIMPORTING DWORD WINAPI GetModuleFileNameExA(HANDLE,HMODULE,LPSTR,DWORD);
DLLIMPORTING DWORD WINAPI GetModuleFileNameExW(HANDLE,HMODULE,LPWSTR,DWORD);

DLLIMPORTING DWORD WINAPI GetModuleBaseNameA(HANDLE hProcess, HMODULE hModule, LPSTR lpBaseName, DWORD nSize);
DLLIMPORTING DWORD WINAPI GetModuleBaseNameW(HANDLE hProcess,HMODULE hModule,LPWSTR lpwBaseName,DWORD nSize);

#ifdef UNICODE
#define GetModuleFileNameEx GetModuleFileNameExW
#define GetModuleBaseName GetModuleBaseNameW
#else
#define GetModuleFileNameEx GetModuleFileNameExA
#define GetModuleBaseName GetModuleBaseNameA
#endif

//////////////////////

// To ensure correct resolution of symbols, add Psapi.lib to TARGETLIBS
// and compile with -DPSAPI_VERSION=1
void PrintProcessNameAndID( DWORD processID )
{
      TCHAR szProcessName[MAX_PATH] = TEXT("<unknown>");

      // Get a handle to the process.
      HANDLE hProcess = OpenProcess( PROCESS_QUERY_INFORMATION |
      PROCESS_VM_READ,
      FALSE, processID );

    // Get the process name.
    if (NULL != hProcess )
    {
        HMODULE hMod;
        DWORD cbNeeded;

        if ( EnumProcessModules( hProcess, &hMod, sizeof(hMod), &cbNeeded) )
        {
              GetModuleBaseName( hProcess, hMod, szProcessName,
                                                sizeof(szProcessName)/sizeof(TCHAR) );
        }
    }

    // Print the process name and identifier.
    printf( TEXT("%s  (PID: %u)\n"), szProcessName, processID );
    // Release the handle to the process.
    CloseHandle( hProcess );
}

//////////////////////

int main(void)
{
      //////////////////////////

      // Get the list of process identifiers.
      DWORD aProcesses[1024], cbNeeded, cProcesses;
      unsigned int i;

      if ( !EnumProcesses( aProcesses, sizeof(aProcesses), &cbNeeded ) )
      {
            return 1;
      }

      // Calculate how many process identifiers were returned.
      cProcesses = cbNeeded / sizeof(DWORD);

      // Print the name and process identifier for each process.
      for ( i = 0; i < cProcesses; i++ )
      {
            if( aProcesses[i] != 0 )
            {
                  PrintProcessNameAndID( aProcesses[i] );
            }
      }
      system("pause");
      return 0;
}

//

The target program yes requires that static library psapi.lib would be referenced.

The files:

http://imgs.g4estatic.com/dll-wrapper/wrapper6.png

The output:

http://imgs.g4estatic.com/dll-wrapper/xwrapper2.png

See attached project in VC++ 6.


All times are GMT +5.5. The time now is 21:38.