Click here to Skip to main content
Email Password   helpLost your password?

Introduction

A lot of us have tried to create a proxy DLL to replace an existing one and spy other programs' calls. Here is a small program that will create the CPP and DEF for a proxy DLL based on the exports of another DLL. You can use it to generate a template and then edit this template to satisfy your needs.

Background

When creating a proxy DLL, you have to export precisely the same names as exported by the original DLL. This can be painful, for two reasons:

  1. There are too many exports.
  2. There are functions that you don't know what they do; you'd just want to spy on one specific function call.

The second problem is solved with assembly and with the aid of the __declspec(naked) attribute. The program creates function stubs that do nothing but JUMP (not call) to the exported address, so the stack is left as it should be. This allows you to create code only for functions that you actually know what they do.

Using the program

WRAPPIT <dll> <txt> <convention> <point dll name> <cpp> <def>  

Example:

You have WSOCK32.DLL and you want to create a proxy for it, replacing the original DLL as WSOCK32_.DLL. What would you do?

This will:

A single CPP will look like this:

//

#include <windows.h>

#pragma pack(1)
HINSTANCE hLThis = 0;
HINSTANCE hL = 0;
FARPROC p[75] = {0};
// -----------

BOOL WINAPI DllMain(HINSTANCE hInst,DWORD reason,LPVOID)
{
    if (reason == DLL_PROCESS_ATTACH)
    {
        hLThis = hInst;
        hL = LoadLibrary(".\\wsock32_.dll");
        if (!hL) return false;

        p[0] = GetProcAddress(hL,"AcceptEx");
        p[1] = GetProcAddress(hL,"EnumProtocolsA");
        p[2] = GetProcAddress(hL,"EnumProtocolsW");
      ...
    }
    if (reason == DLL_PROCESS_DETACH)
    {
        FreeLibrary(hL);
    }
    return 1;
}

// AcceptEx

extern "C" __declspec(naked) void __stdcall __E__0__()
{
    __asm
    {
        jmp p[0*4];
    }
}

// EnumProtocolsA

extern "C" __declspec(naked) void __stdcall __E__1__()
{
    __asm
    {
        jmp p[1*4];
    }
}

// EnumProtocolsW

extern "C" __declspec(naked) void __stdcall __E__2__()
{
    __asm
    {
        jmp p[2*4];
    }
}
...
//

A single DEF will look like this:

EXPORTS
AcceptEx=__E__0__ @1141
EnumProtocolsA=__E__1__ @1111
EnumProtocolsW=__E__2__ @1112
...

You may now edit CPP/DEF files and reuse them to create your own proxy DLL!

Important!

Once the cpp is ready, you should replace functions that you know how to use. For example, If you want to spy on Wsock32.send():

// send, created by wrappit

extern "C" __declspec(naked) void __stdcall __E__69__()
   {
   __asm
    {
    jmp p[69*4];
    }
 }

// If you want to manipulate it, change to:

extern "C" int __stdcall __E__69__(SOCKET x,char* b,int l,int pr)
  {
  // manipulate here parameters


.....
  // call original send

     typedef int (__stdcall *pS)(SOCKET,char*,int,int);
     pS pps = (pS)p[63*4];
     int rv = pps(x,b,l,pr);

     return rv;
  }

History

You must Sign In to use this message board.
 
 
Per page   
 FirstPrevNext
Generalwhy p[x*4] ?
daDotr
8:11 19 Dec '09  
First, big thx for your tool. Smile

But you wrote:
     typedef int (__stdcall *pS)(SOCKET,char*,int,int);
pS pps = (pS)p[63*4];
int rv = pps(x,b,l,pr);

I need to do this:
     typedef int (__stdcall *pS)(SOCKET,char*,int,int);
pS pps = (pS)p[63];
int rv = pps(x,b,l,pr);

Then everything works fine.
GeneralExports having a ordinal number only
spackjarrow
11:53 9 Dec '09  
First: Great tool/code, really helped me out!

Line 204:
//			fprintf(fcpp,"\t\tp[%u] = GetProcAddress(hL,(LPCSTR)\"%u\");\r\n",i,v[i].o);
fprintf(fcpp,"\t\tp[%u] = GetProcAddress(hL,(LPCSTR)%u);\r\n",i,v[i].o);

Should be changed (remove quotes around ordinal number), otherwise GetProcAddress will return 0;

Also, in Visual Studio Express 2005 some warnings occur when the dll has one of the following functions exported:

DllCanUnloadNow,DllGetClassObject,DllInstall,DllRegisterServer,DllUnregisterServer

Those functions should always be private (why?) and are never loaded by ordinal value.

In the DEF file replace
DllCanUnloadNow=__E__4__ @163
by
DllCanUnloadNow=__E__4__ PRIVATE

GeneralI cant make it work for ws2_32.dll
Mugiwara
5:44 27 Jul '09  
When i try to make a proxy for ws2_32.dll with your tool, i get the following error:

Entry Point Not Found - The procedure entry point wsagetlasterror could not be located in the dynamic link library wsock32.dll
So i tried to proxy wsock32.dll too but then the program says it could not be initialized.

Any idea about what happening ?
GeneralRe: I cant make it work for ws2_32.dll
ArielMendoza
22:29 27 Jul '09  
Check the parameters of the linker (/def)
GeneralRe: I cant make it work for ws2_32.dll
Mugiwara
7:03 28 Jul '09  
"/def" ??

Is that a command i did not see and missed or are you talking about the generated def file? I work with visual C++ 8.
GeneralRe: I cant make it work for ws2_32.dll
ArielMendoza
10:59 28 Jul '09  
http://msdn.microsoft.com/en-us/library/28d6s79h.aspx[^]

The /DEF option passes a module-definition file (.def) to the linker. Only one .def file can be specified to LINK. For details about .def files, see Module-Definition Files.

To set this linker option in the Visual Studio development environment
Open the project's Property Pages dialog box. For details, see Setting Visual C++ Project Properties.

Click the Linker folder.

Click the Input property page.

Modify the Module Definition File property.

To specify a .def file from within the development environment, you should add it to the project along with other files and then specify the file to the /DEF option.
GeneralRe: I cant make it work for ws2_32.dll [modified]
Mugiwara
3:24 29 Jul '09  
Any idea about this means ?

http://img228.imageshack.us/img228/7624/again.jpg

When the program loads the proxy DLL this error occures.

modified on Wednesday, July 29, 2009 11:15 AM

GeneralRe: I cant make it work for ws2_32.dll
ArielMendoza
11:40 29 Jul '09  
Is this the same machine where you installed Visual Studio?
Send me the Project and revised it.
GeneralNew application
ArielMendoza
1:34 27 Jul '09  
Hi, I created this application based on the above to remove the dependencies to other tools (dumpbin). It is a test and can be improved. Laugh
Regards.

//email: arielmendoza@hotmail.com
#include "stdafx.h" #include "stdafx.h" #include "windows.h" #include "winnt.h" #include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include "string.h"

int main(int argc, char* argv[])
{
char DefFile[255];
char CppFile[255];

if(argc!=3)
{
printf("\nMissing parameters ex:");
printf("\nExtractDllExports ws2_32.dll ws2_32");
return 2;
}
memset(DefFile,0,sizeof(DefFile));
memset(CppFile,0,sizeof(CppFile));
sprintf(DefFile,"%s.def",argv[2]);
sprintf(CppFile,"%s.cpp",argv[2]);
FILE *fpdef;
FILE *fpcpp;
if((fpdef=fopen(DefFile, "w+"))==NULL)
{
printf("\Error in CreateFile %s",DefFile);
return 1;
}
if((fpcpp=fopen(CppFile, "w+"))==NULL)
{
printf("\Error in CreateFile %s",CppFile);
return 1;
}


HMODULE lib = LoadLibraryExA(argv[1], NULL, DONT_RESOLVE_DLL_REFERENCES);
if(lib==NULL)
{
//Error cargando la DLL
printf("\nError in LoadLibraryExA. Dll:%s",argv[1]);
return 1;
}
assert(((PIMAGE_DOS_HEADER)lib)->e_magic == IMAGE_DOS_SIGNATURE);
PIMAGE_NT_HEADERS header =PIMAGE_NT_HEADERS((BYTE *)lib + ((PIMAGE_DOS_HEADER)lib)->e_lfanew);
assert(header->Signature == IMAGE_NT_SIGNATURE);
assert(header->OptionalHeader.NumberOfRvaAndSizes > 0);
PIMAGE_EXPORT_DIRECTORY exports = PIMAGE_EXPORT_DIRECTORY((BYTE *)lib + header->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);
PVOID names = (BYTE *)lib + exports->AddressOfNames;
WORD *Ordinals = (WORD*)((BYTE *)lib + exports->AddressOfNameOrdinals);
//inicializar el .cpp
fputs("\n//**** remember to add the /def parameter to linker ****",fpcpp);
fputs("\n#include <windows.h>",fpcpp);
fputs("\n\nHINSTANCE hLThis = 0;",fpcpp);
fputs("\nHINSTANCE hL = 0;",fpcpp);
char Farproc[255];
memset(Farproc,0,sizeof(Farproc));
sprintf(Farproc,"\nFARPROC p[%d] = {0};",exports->NumberOfNames);
fputs(Farproc,fpcpp);
fputs("\nBOOL WINAPI DllMain(HINSTANCE hInst,DWORD reason,LPVOID)",fpcpp);
fputs("\n{",fpcpp);
fputs("\n if (reason == DLL_PROCESS_ATTACH)",fpcpp);
fputs("\n {",fpcpp);
fputs("\n hLThis = hInst;",fpcpp);
char DllName[255];
memset(DllName,0,sizeof(DllName));
sprintf(DllName,"\n hL = LoadLibrary(\"%s\");",argv[1]);
fputs(DllName,fpcpp);
fputs("\n if (!hL) return false;",fpcpp);

//inicializar el ,def
fputs("EXPORTS",fpdef);
for (int i = 0; i < exports->NumberOfNames; i++)
{
//Escribir el .def
char txtFunction[255];
memset(txtFunction,0,sizeof(txtFunction));
WORD w=(WORD )(BYTE *)lib + ((WORD *)Ordinals)[i]+1;
sprintf(txtFunction,"\n%s=__E__%d__ @%d", (BYTE *)lib + ((DWORD *)names)[i],i,w);
fputs(txtFunction,fpdef);
//Cpp
char txtPointer[255];
memset(txtPointer,0,sizeof(txtPointer));
sprintf(txtPointer,"\n p[%d] = GetProcAddress(hL,\"%s\");",i,(BYTE *)lib + ((DWORD *)names)[i]);
fputs(txtPointer,fpcpp);

}
fputs("\n }",fpcpp);
fputs("\n if (reason == DLL_PROCESS_DETACH)",fpcpp);
fputs("\n {",fpcpp);
fputs("\n FreeLibrary(hL);",fpcpp);
fputs("\n }",fpcpp);
fputs("\n return 1;",fpcpp);
fputs("\n}",fpcpp);
//Comentario de como modificar la funcion
fputs("\n// gethostname",fpcpp);
fputs("\n// Example replace functions ",fpcpp);
fputs("\n//extern \"C\" int __stdcall __E__92__(char *name, int namelen)",fpcpp);
fputs("\n// {",fpcpp);
fputs("\n//call original gethostname",fpcpp);
fputs("\n// typedef int (__stdcall *pS)(char*,int);",fpcpp);
fputs("\n// pS pps = (pS)p[92];",fpcpp);
fputs("\n// int rv = pps(name,namelen);",fpcpp);
fputs("\n// if(rv==0)",fpcpp);
fputs("\n// {",fpcpp);
fputs("\n// memset(name,0,namelen);",fpcpp);
fputs("\n//Manipulate result",fpcpp);
fputs("\n// strcpy(name,\"TestName\");",fpcpp);
fputs("\n// }",fpcpp);
fputs("\n// return rv;",fpcpp);
fputs("\n// }",fpcpp);
fputs("\n//end example;",fpcpp);
// for (int i = 0; i < exports->NumberOfNames; i++)
{
char Comentario[255];
char Encabezado[255];
char Salto[255];
memset(Comentario,0,sizeof(Comentario));
memset(Encabezado,0,sizeof(Encabezado));
memset(Salto,0,sizeof(Salto));
sprintf(Comentario,"\n\n//%s",(BYTE *)lib + ((DWORD *)names)[i]);
sprintf(Encabezado,"\nextern \"C\" __declspec(naked) void __stdcall __E__%d__()",i);
sprintf(Salto,"\n jmp p[%u*%u];",i,sizeof(void*));
fputs(Comentario,fpcpp);
fputs(Encabezado,fpcpp);
fputs("\n{",fpcpp);
fputs("\n __asm",fpcpp);
fputs("\n {",fpcpp);
fputs(Salto,fpcpp);
fputs("\n }",fpcpp);
fputs("\n}",fpcpp);

}
//system("pause");
fcloseall();
return 0;
}



GeneralError in the index
CodeGibbon
2:58 3 Dec '08  
There is an error in the last example. It must not be

pS pps = (pS)p[63*4];

but

pS pps = (pS)p[69];

Someone in previous comments got access violation - it is due to this error, not because "(naked)" or something else.
GeneralForwarding function
sparrowIsaBird
14:12 22 Oct '08  
Hi!

I am new to c++ programming so this question might sound silly.

I am trying to manipulate the parameters and proxy this function:

D3DXMATRIX* WINAPI D3DXMatrixLookAtRH
( D3DXMATRIX *pOut, CONST D3DXVECTOR3 *pEye, CONST D3DXVECTOR3 *pAt,
CONST D3DXVECTOR3 *pUp );

And the code from my proxy dll looks like this:

extern "C" D3DXMATRIX* WINAPI __stdcall __E__205__(D3DXMATRIX *pOut,CONST D3DXVECTOR3 *pEye, CONST D3DXVECTOR3 *pAt, CONST D3DXVECTOR3 *pUp)
{

typedef D3DXMATRIX* (WINAPI __stdcall *pS)(D3DXMATRIX*, CONST D3DXVECTOR3*, CONST D3DXVECTOR3*, CONST D3DXVECTOR3*);
pS pps = (pS)p[205*4];
D3DXMATRIX* rv = pps(pOut,pEye,pAt,pUp);

return rv;
}

I get an unhandled exception. Any idea what am I doing wrong?
GeneralRe: Forwarding function
Michael Chourdakis
21:32 22 Oct '08  
At first glance, I don't see anything wrong. Do ALL functions crash ? If not, then chances are that you calling the function with bad parameters.

1. Is it for a 64-bit dll ? (then you need 205*8!)
2. WINAPI = __stdcall so you do not need both.
3. Check the value of the rv while debugging.
GeneralRe: Forwarding function
sparrowIsaBird
0:25 23 Oct '08  
For the first test I just pass on the parameters on and don't manipulate them. What is interesting if I do this:
(debugging it)
extern "C" D3DXMATRIX* __stdcall __E__205__(D3DXMATRIX *pOut,CONST D3DXVECTOR3 *pEye, CONST D3DXVECTOR3 *pAt, CONST D3DXVECTOR3 *pUp)
{

std::ofstream outdata;
outdata.open("C:\\out.txt");
outdata << "Eye:" << pEye->x << " " << pEye->y << " " << pEye->z << std::endl;
outdata.close();
return NULL;
}

The function calls seem to succeed. Because the parameters do have the right values. So I am shore the function is called.

Maybe it has something to do with the first function parameter being the [in, out] parameter? The function out parameter and the returning type are the same. (according to documentation: http://msdn.microsoft.com/en-us/library/bb205343(VS.85).aspx[^])

if I put the rest of the function in I get an unhandled exception at this line of code:
D3DXMATRIX* rv = pps(pOut,pEye,pAt,pUp);

GeneralCant create exports.txt :'(
Member 4343919
8:00 15 Jan '08  
When i try to use "proxy2.exe dumpbin /exports wsock32.dll > exports.txt" i get the following written into the exports.txt :

Wrappit. Copyright (C) Chourdakis Michael

Usage: WRAPPIT
==================================================================



I tried diffrent dlls but it didnt work either...

Thanks for answers

Thelod

GeneralRe: Cant create exports.txt :'(
Michael Chourdakis
9:25 15 Jan '08  
Hi there.
Be patient for a few days and I will put wrappit2.cpp which has many bugs fixed.

Michael.
GeneralRelease of Wrappit 2.0
Michael Chourdakis
8:14 16 Dec '07  
I 've created a second version which works without inline assembly, by using forwards - and it works with PPC and x64. If anyone interested, mail me , I should update the CP article soon.
GeneralRe: Release of Wrappit 2.0
Hernán Di Pietro
19:36 17 Sep '08  
Hey, it's a very handy tool, excellent work.

Can you email me your new version?

Thank you very much.

visit me at
http://usuarios.lycos.es/hernandp

GeneralRe: Release of Wrappit 2.0
ndataman
6:18 21 Oct '08  
Thanks for sharing your code with us. Can you send me version 2 please.

I can't make work v1 correctly with borland compiled dll. The original dll uses DLLEntyPoint, which seems to cause the calling application to crash when using the proxy dll.

Another issue in the original code is that the generated name of all functions was always the same because i was not incremented while parsing .
GeneralRe: Release of Wrappit 2.0
Michael Chourdakis
6:28 21 Oct '08  
Hi there.

http://tinyurl.com/68fax6[^] has the wrappit2.cpp.

Good luck.
GeneralRe: Release of Wrappit 2.0
ndataman
1:18 22 Oct '08  
Thank you for the quick reply Michael.
GeneralRe: Release of Wrappit 2.0
top_sli
13:31 14 Feb '10  
A PM sent to your account. Have some problems with using wrappit 2.
GeneralHow to make it work with WinCE? [modified]
Kelvin Foo Chuan Lyi
17:00 10 Sep '07  
WinCE (ARMV4) doesn't support inline assembly. Is there an alternative code to make the stub cpp file compile with WinCE? I don't know most of the parameters for the DLL so i was hoping there's another method to do the jmp.

Thanks


-- modified at 23:23 Monday 10th September, 2007
GeneralRe: How to make it work with WinCE?
Michael Chourdakis
18:15 10 Sep '07  
There is a way to create a 'forward entry' in a dll, but i am not sure how.

For example, if you see wsock32.dll , many items are automatically forwarded to ws2_32.dll.
In case you find the relative linker/def file option, let me know Smile


GeneralRe: How to make it work with WinCE?
Kelvin Foo Chuan Lyi
6:02 14 Sep '07  
Thanks for the tip. I manage to find it out.

First you need to create a .LIB from the original DLL
LIB /MACHINE:X86 /DEF:original.def

where original.def goes something like this
LIBRARY original
EXPORTS
ExpFunc1 @1 NONAME
ExpFunc2 @2 NONAME
...
WlxShutdown @48
WlxStartApplication @49
WlxWkstaLockedSAS @50


Then you need to create a forwarding .def file which goes into your project and goes something like this
LIBRARY stub
EXPORTS
ExpFunc1=original.@1 @1 NONAME
ExpFunc2=original.@2 @2 NONAME
...
WlxShutdown=original.WlxShutdown @48
WlxStartApplication=original.WlxStartApplication @49
WlxWkstaLockedSAS=original.WlxWkstaLockedSAS @50

Also, link the LIB file which you have created previously.

Thats all.

GeneralRe: How to make it work with WinCE?
Michael Chourdakis
6:10 14 Sep '07  
If this works, I will change my article so all inline assembly can be avoided.

M.X.


Last Updated 14 May 2007 | Advertise | Privacy | Terms of Use | Copyright © CodeProject, 1999-2010