While learning to write DLLs, I found it difficult to find a simple straight forward tutorial which outlined just the basics of creating and compiling a DLL, particularly when using the Dev-C++ compiler. DLLs present some interesting possibilities to a programmer, especially when injecting code into another application's memory, or modifying a closed-source program's functionality. I will cover writing exportable functions as well as running code when the DLL is injected into a process. You should have basic knowledge of C and Win32 applications.
[~] Dev-C++ - Free C/C++
IDE application.
[~] DLL_EX.zip - Sample
DLL files (not required).
First, open Dev-C++ and create a new project. Select 'DLL' under the Basic tab, and make sure the 'C Project' option is selected. Name it whatever you want and hit OK:
An exportable function is one which an executable can load into memory (i.e., it can be exported from the DLL) and make availiable to its code; essentially, you are creating your own API function (see my tutorial on adding DLL functions to closed-source binaries for a more detailed explanation). After performing the steps above, Dev-C++ automatically creates two skeleton files, dllmain.c and dll.h; let's take a look at the contents of dll.h:
#ifndef _DLL_H_
#define _DLL_H_
#if BUILDING_DLL
# define DLLIMPORT __declspec (dllexport)
#else /* Not BUILDING_DLL */
# define DLLIMPORT __declspec (dllimport)
#endif /* Not BUILDING_DLL */
DLLIMPORT void HelloWorld (void);
#endif /* _DLL_H_ */
A program can import this function, then make a call to 'HelloWorld()' which will in turn open a message box. There is also a skeleton DllMain function, which can be ignored/removed if you are writing the DLL with only the intent to create an exportable function.
DLLIMPORT void HelloWorld ()
{
MessageBox (0, "Hello World from DLL!\n", "Hi", MB_ICONINFORMATION);
}
Once a DLL is injected into a process, its DLLMain function is called. DllMain is the equivelant to any C program's main() funciton. Dev-C++'s dllmain.c file contains a skeleton DllMain function:
When a DLL is injected into an application's memory area, DllMain is called with the reason of DLL_PROCESS_ATTACH, so we will be using that particular case to initiate our code. Next, we need to load one of the DLL's functions as a thread of the application which the DLL has been injected into, so let's create a simple Hello World function:
BOOL APIENTRY DllMain (HINSTANCE hInst /* Library instance handle. */ ,
DWORD reason /* Reason this function is being called. */ ,
LPVOID reserved /* Not used. */
)
{
switch (reason)
{
case DLL_PROCESS_ATTACH: /*We will be using this case*/
break;
case DLL_PROCESS_DETACH:
break;
case DLL_THREAD_ATTACH:
break;
case DLL_THREAD_DETACH:
break;
}
/* Returns TRUE on success, FALSE on failure */
return TRUE;
}
To run this function as a thread, we will call CreateThread from within the DllMain function:
void HelloWorldMsg ()
{
MessageBox (0, "Hello World from DLL!\n", "Hi", MB_ICONINFORMATION);
}
hThread = CreateThread(
NULL, // Security
0, // Stack size
HelloWorldMsg, // Function to create as a thread
NULL, // Parameter
0, // Disposition
&nThread // Handle value
);
The dllmain.c file should now look like this:
#include "dll.h"
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
DLLIMPORT void HelloWorld ()
{
MessageBox (0, "Hello World from DLL!\n", "Hi", MB_ICONINFORMATION);
}
void HelloWorldMsg ()
{
MessageBox (0, "Hello World from an injected DLL!\n", "Hi", MB_ICONINFORMATION);
}
BOOL APIENTRY DllMain (HINSTANCE hInst /* Library instance handle. */ ,
DWORD reason /* Reason this function is being called. */ ,
LPVOID reserved /* Not used. */ )
{
DWORD nThread; // Thread value
HANDLE hThread; // Thread handle
switch (reason)
{
case DLL_PROCESS_ATTACH:
if((hThread = CreateThread(NULL,0,HelloWorldMsg,NULL,0,&nThread)) != NULL){
// Close handle
CloseHandle(hThread);
}
break;
case DLL_PROCESS_DETACH:
break;
case DLL_THREAD_ATTACH:
break;
case DLL_THREAD_DETACH:
break;
}
/* Returns TRUE on success, FALSE on failure */
return TRUE;
}
The easiest way to quickly test our DLL is to place it in the C:\Windows\system32 directory and add its name to the HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Windows\AppInit_DLLs key:
"The AppInit DLLs are loaded by using the LoadLibrary() function during the DLL_PROCESS_ATTACH process of User32.dll."
In other words, all programs that load User32.dll (i.e., nearly all Win32 applications) will also load any DLL specified in this registry key. Once you have added the name of your dll, run any program such as calc.exe or notepad.exe, and you should recieve a "Hello World from an injected DLL!" messagebox.
The ability to write exportable functions, or to injected a DLL into other processes and run code from within that proccess's memory presents some very interesting consequences. For instance, DLL injection can be used to bypass personal firewalls, and importing DLL functions is an easy way to add functionality to closed-source applications.
Copyright ©2006 craigheffner.com