The intended use of the shim DLL is to drop it into the directory of a specific executable. The executable will (in most cases) load the shim DLL instead of the real one. By default, all functions in the shim DLL call (actually jmp) straight through to the function in the real DLL in the Windows system directory (system32). You should do this first, to verify that the basic shim DLL works transparently.
Now you can get up to mischief by replacing individual _I_BAR functions with functions with the correct signatures, and call (rather than jmp) through to the real functions, for which you'll need to replace the _O_BAR() function pointer with one with the correct signature. This allows you to view or modify both the parameters, and the return value. The uses for this are practically endless; just for example, you can intercept all IP traffic from an application (which Ethereal already allows) but you can also modify the connections, and the incoming or outgoing data.
If you are feeling particularly confident, you could even replace the real system DLL with the shim one, so that all applications will use it. I do not recommend that you do this, as if you pooch it, you might completely stuff your Windows install.
The code follows. Save it out and compile it (modifying the CLONE_DLL and/or INSERT_DEBUG_OUTPUT defines first) and run the resulting console executable. All being well, it will produce a .cpp and .def file. Use them to build the actual shim DLL, modifying them as required. In Microsoft Dev Studio, it's as simple as creating a new empty DLL project and including the FOO.cpp and FOO.def files.
I've tested this by shimming WS2_32.dll, which has a fairly hefty set of exports, and it works as expected. Bug reports and improvements welcome.
#include <stdio.h>
#include <windows.h>
#include <list>
using namespace std;
// Change this to the name of the DLL that you want to clone
#define CLONE_DLL "WS2_32"
// Define this to insert basic fprintf debug into the cloned DLL
#define INSERT_DEBUG_OUTPUT 1
typedef struct
{
int ordinal;
char name[64];
} Symbol;
static list<Symbol> gs_symbols;
main()
{
char originalDLL[256];
(void)GetSystemDirectory(originalDLL, sizeof(originalDLL) - 1);
strcat(originalDLL, "\\" CLONE_DLL ".dll");
char exportFile[256];
(void)GetTempPath(sizeof(exportFile), exportFile);
strcat(exportFile, "exports.txt");
char dumpbin[512];
sprintf(dumpbin, "dumpbin /exports %s > %s", originalDLL, exportFile);
(void)system(dumpbin);
FILE * exports = fopen(exportFile, "r");
FILE * cpp = fopen(CLONE_DLL ".cpp", "w");
FILE * def = fopen(CLONE_DLL ".def", "w");
while(!feof(exports))
{
char line[256];
Symbol newSymbol;
if(fgets(line, sizeof(line) - 1, exports))
if(2 == sscanf(line, "%d %*x %*x %s",
&newSymbol.ordinal,
newSymbol.name))
gs_symbols.push_back(newSymbol);
}
fprintf(def, "LIBRARY " CLONE_DLL "\n\nEXPORTS\n");
for(list<Symbol>::iterator symbol = gs_symbols.begin();
symbol != gs_symbols.end();
++symbol)
fprintf(def, "\t%s = _I_%s @%d\n",
symbol->name,
symbol->name,
symbol->ordinal);
fprintf(cpp, "#include <stdio.h>\n");
fprintf(cpp, "#include <windows.h>\n");
fprintf(cpp, "static HINSTANCE gs_hDLL = 0;\n\n");
#if INSERT_DEBUG_OUTPUT
fprintf(cpp, "#if defined (_DEBUG)\n");
fprintf(cpp, "static FILE * debug;\n");
fprintf(cpp, "#define DEBUG(x) fprintf x\n");
fprintf(cpp, "#else // DEBUG\n");
fprintf(cpp, "#define DEBUG(x)\n");
fprintf(cpp, "#endif // _DEBUG\n\n");
#endif // INSERT_DEBUG_OUTPUT
for(symbol = gs_symbols.begin();
symbol != gs_symbols.end();
++symbol)
{
fprintf(cpp, "int (__stdcall * _O_%s)();\n", symbol->name);
fprintf(cpp, "extern \"C\" void __declspec(naked) __stdcall _I_%s()\n", symbol->name);
fprintf(cpp, "{\n");
#if INSERT_DEBUG_OUTPUT
fprintf(cpp, "\tDEBUG((debug, \"%s\\n\"));\n", symbol->name);
#endif // INSERT_DEBUG_OUTPUT
fprintf(cpp, "\t__asm { jmp _O_%s }\n", symbol->name);
fprintf(cpp, "}\n\n");
}
fprintf(cpp, "BOOL WINAPI DllMain(HINSTANCE hI, DWORD reason, LPVOID notUsed)\n");
fprintf(cpp, "{\n");
fprintf(cpp, " if (reason == DLL_PROCESS_ATTACH)\n");
fprintf(cpp, " {\n");
#if INSERT_DEBUG_OUTPUT
fprintf(cpp, "#if defined(_DEBUG)\n");
fprintf(cpp, " if(!debug)\n");
fprintf(cpp, " debug = fopen(\"debug.txt\", \"w\");\n");
fprintf(cpp, "#endif // DEBUG\n");
fprintf(cpp, " DEBUG((debug, \"DllMain\\n\"));\n\n");
#endif // INSERT_DEBUG_OUTPUT
fprintf(cpp, " char realDLL[MAX_PATH];\n");
fprintf(cpp, " (void)GetSystemDirectory(realDLL, sizeof(realDLL) - 1);\n");
fprintf(cpp, " strcat(realDLL, \"\\\\" CLONE_DLL ".dll\");\n");
fprintf(cpp, " gs_hDLL = LoadLibrary(realDLL);\n");
fprintf(cpp, " if (!gs_hDLL)\n");
fprintf(cpp, " return FALSE;\n\n");
for(symbol = gs_symbols.begin();
symbol != gs_symbols.end();
++symbol)
{
fprintf(cpp, " _O_%s = GetProcAddress(gs_hDLL, \"%s\");\n",
symbol->name, symbol->name);
}
fprintf(cpp, "\n return TRUE;\n");
fprintf(cpp, " }\n");
fprintf(cpp, " else if (reason == DLL_PROCESS_DETACH)\n");
fprintf(cpp, " {\n");
#if INSERT_DEBUG_OUTPUT
fprintf(cpp, "#if defined(_DEBUG)\n");
fprintf(cpp, " if(debug)\n");
fprintf(cpp, " fclose(debug);\n");
fprintf(cpp, "#endif // DEBUG\n\n");
#endif // INSERT_DEBUG_OUTPUT
fprintf(cpp, " FreeLibrary(gs_hDLL);\n\n");
fprintf(cpp, " }\n");
fprintf(cpp, " return TRUE;\n");
fprintf(cpp, "}\n\n");
fclose(exports);
fclose(cpp);
fclose(def);
return 0;
}
| < I'm a little teapot | BBC White season: 'Rivers of Blood' > |

