Logo
blank Skip to main content

Windows DLL Injection Into Process Using KnownDlls

C++

In this article, we will consider an interesting, universal, and rarely used method of DLL injection into a Windows process using KnownDlls sections. To demonstrate the method work we will develop a Windows-based sample project to inject DLL into all running processes and intercept some calls from ws2_32.dll.

Windows process injection supposes injection of a custom code into the address space of some processes. In other words, we get access to the process code, its data, the code of the system DLLs, which are loaded to the process, etc.

Why to inject DLL into process? There can be a lot of causes, both destructive โ€“ steal passwords, hack protected application โ€“ and peaceful ones: like antivirus analysis and protection, improvement and maintenance of an application, which source code you donโ€™t have.

What is KnownDlls

KnownDLL is a Windows mechanism to cache frequently used system DLLs. Initially, it was added to accelerate application loading, but also it can be considered as a security mechanism, as it prevents malware from putting Trojan versions of system DLLs to the application folders (as all main DLLs belong to KnownDLLs, the version from the application folder will be ignored). We cannot say that this security mechanism is very strong (if you have permission to write to the application folder, you can create much more โ€œtools of chaosโ€), but still it helps to protect the system.

Letโ€™s consider its work. When the loader comes across a record about DLL import in an executive file, it opens the file and tries to map it to the memory. But there are some nuances. In practice, before it happens, OS loader searches for the section (of MMF type) named KnownDlls<file-name-DLL>. If this section exists, then instead of opening the file the loader just use the mentioned section, i.e. maps the section to the process address space. Then it continues in accordance with the โ€œclassicalโ€ DLL loading rules.

If you compare the key

C
KEY_LOCAL_MACHINESystemCurrentControlSetControlSession  ManagerKnownDLLs

with the KnownDlls sections, youโ€™ll notice that the KnownDlls container always includes more records than the mentioned registry key. Itโ€™s because the sections in KnownDlls are the result of the transitive closure of all DLLs listed in the KnownDLLs. I.e. if a DLL is mentioned in KnownDLLs, then all the DLLs, which are statically connected with it, are also included to the KnownDlls sections.

Moreover, if you look closer to the KnownDLLs registry key, youโ€™ll see that the search paths are not indicated there. Itโ€™s because all KnownDLLs are supposed to be located in the folder, indicated in the registry key

C
KEY_LOCAL_MACHINESystemCurrentControlSetControlKnownDLLsDllDirectory. 

This is one more security aspect of KnownDLLs: requirement of that all KnownDLLs are placed in the same specific folder.

When the system is loading, it looks for the path in the registry

C
KEY_LOCAL_MACHINESystemCurrentControlSetControlSession  ManagerKnownDLLs

and creates the sections KnownDlls<file-name-DLL> for each DLL, listed in this registry key.

It should be mentioned that starting with Windows Vista itโ€™s impossible to add directly a string parameter with the DLL path to the KnownDLLs registry hive, as the system protects this hive from record. But if the application is started with the admin permissions, the user can get the permission to write to this hive.

This article is devoted namely to the method of Windows DLL injection using the described mechanism.

Pros of the method of intrusion using KnownDlls:

  • We tested this method and confirmed its work on the Windows versions starting with XP and up to WinServer 2008 R2.
  • We can hook the calls from the substituted DLL without the called function code change. It is very important for those applications, where the code integrity is checked by means of CRC.
  • We can intrude into the protected processes with no problems.
  • We can substitute and hook system DLLs calls.
  • At the moment of this article release, this method was detected only by Kaspersky CRYSTAL Antivirus.

The main shortcoming is that the undocumented APIs are used.

Tech Aspects

Now letโ€™s proceed to the technology in detail.

In our Windows DLL injection function example, we will inject custom code to the web browser as a target application. We work with the Windows7 x64 OS and use Mozilla Firefox, although this method will also work for any other browser that starts as the x32 application.

Expected result: replace the text โ€œWrong_codeโ€ with the text โ€œCool_code!โ€ in all browser requests and server responses. For example, if we enter the search request with the โ€œWrong_codeโ€ text to the search line of our favorite search engine and click the button to start search, the browser forms an HTTP request to the server, and there, our sample will replace the mentioned text with the โ€œCool_code!โ€ and only after that it will be sent to the server. The same actions are performed when the server response is obtained.

Scheme of function redirection

Scheme of function redirection.

To implement the above mentioned functionality, weโ€™ll substitute the section with the native ws2_32.dll with the section with our DLL. In the sample, the substitution is performed in the x32 process, i.e. ws2_32.dll from the C:WindowsSysWOW64 folder. To get the path to the SysWOW64, you can call the SHGetSpecialFolderPathW function.

In our sample, weโ€™ll hook the send and recv calls of winsock and provide C++ code injection.

So, letโ€™s start.

Call Redirection

First, we create DLL for substitution during other DLL hooking. It will have the same name as the native one – ws2_32.dll.

Creating the stub (inside the DLL for substitution). The idea consists in create export table in dll stub, which functions called will be redirect in original dll. Functions which we must hook we will implement in our application and exporting them us usually.

The main goal is achieved using #pragma comment compiler directive. It has several parameters, including EXPORT (verbose about this instruction you can read here: http://msdn.microsoft.com/en-us/library/7f0aews7.aspx). Thus, using a string:

C
#pragma comment(linker,  "/export:<src>=<dst>,@<ord>") 

where

<src> – name of the function to redirect;
<dst> – name of the module and function to be called after redirection;
<ord> – function ordinal number,

we can redirect all necessary calls from our .dll module to the original one.

Using this method we create the file of redirections named redirection.h. It looks as follows.

C
#pragma comment(linker, "/export:FreeAddrInfoEx=wsNative.FreeAddrInfoEx,@25")
  #pragma comment(linker, "/export:FreeAddrInfoExW=wsNative.FreeAddrInfoExW,@26")
  #pragma comment(linker, "/export:FreeAddrInfoW=wsNative.FreeAddrInfoW,@27")
  #pragma comment(linker, "/export:GetAddrInfoExA=wsNative.GetAddrInfoExA,@28")
  #pragma comment(linker, "/export:GetAddrInfoExW=wsNative.GetAddrInfoExW,@29")
  #pragma comment(linker, "/export:GetAddrInfoW=wsNative.GetAddrInfoW,@30")
  #pragma comment(linker, "/export:GetNameInfoW=wsNative.GetNameInfoW,@31")
  #pragma comment(linker, "/export:InetNtopW=wsNative.InetNtopW,@32")

The first string shows that we export the FreeAddrInfoEx function and it will be redirected to the call of FreeAddrInfoEx from wsNative.dll. It will be exported with the ordinal. Why wsNative? Itโ€™s because if we use ws2_32.FreeAddrInfoEx, then the loader will try to load the function from our DLL. The order of listing of the #pragma comment strings is not important, as the linker will sort the functions in the export table by names and letter case.

To create a stub for the native DLL we need to export all the function of the native DLL and moreover, the ordinals of these functions must coincide.

How weโ€™ll make the native ws2_32.dll to be called by the name โ€œwsNative.dllโ€? It is easy, we will create a symbolic link to the ws2_32.dll in its directory by means of CreateSymbolicLink. For example, if native DLL is located in C:WindowsSysWOW64ws2_32.dll, we will create the link with the C:WindowsSysWOW64wsNative.dll path name, which points to C:WindowsSysWOW64ws2_32.dll.

DLL Function Hooking

Then we initialize the addresses of the hooked functions from the native DLL. To implement that, we first define the function types:

C
typedef int (WINAPI* PSEND)(SOCKET s, const char FAR *buf, int len, int flags);
typedef int (WINAPI* PRECV)(SOCKET s, char FAR *buf, int len, int flags); 
Then we load the original DLL using LoadLibraryW, and that simply initialize them:
PSEND       g_pSend     = NULL;
PRECV       g_pRecv     = NULL;
template<typename T>
void InitAddr(T& destAdrr, LPCSTR funcName, HMODULE hModule)
{
    destAdrr = (T)GetProcAddress(hModule, funcName);
    CHECK_ADDRESS(destAdrr);
}
void Init(HMODULE hModule)
{
    InitAddr(g_pSend, "send", hModule);
    InitAddr(g_pRecv, "recv", hModule);
}

Now itโ€™s time to implement the custom functions (analogues for the substituted ones), which weโ€™ll redirect calls to. Both functions get as parameters the buffer buf of the length len.

The send function gets the buffer of the browser request to the server as buf, and for the recv function, the buf buffer represents the response of server. So, itโ€™s enough for us to search and replace the necessary text in buf.

For example:

C
res = std::search(res, buf+len, &searchString[0], &searchString[0] + MAXSTRINGLEN(searchString));
        if (res < buf+len)
        {
            memcpy(const_cast<char*>(res), replaceString, MAXSTRINGLEN(replaceString));
        }

After the above described functions finished their work, we need to call the same functions from the native DLL with the modified parameters.

C
return g_pSend(s, buf, len, flags);
  return g_pRecv(s, buf, len, flags);

And finally, we need to export our functions for hooking. Export can be performed in different ways, but in our case, itโ€™s required that our functions are exported by the same names as in the native DLL. So, we create the def file:

C
LIBRARY
  EXPORTS
      send=_send @19
      recv=_recv @16

Here we can see that send is the name, which our function _send will be exported with, and just like that recv=_recv. The ordinal of the function is indicated after the ยซ@ยป symbol.

Install/uninstall

Now we can start to implement the loader for our section. We implement the service that runs at the system start and add our section. To install the section we need the Create permanent shared object permission and so the service with the system permissions fits us perfectly. Service installation is a very simple thing, so letโ€™s start with the section creation.

Section Creation

– First we open the file of our DLL.

– Then we open the directory of KnownDlls Object Manager using the NtOpenDirectoryObject, function, whose parameter is the name of the directory. For the x32 process section name is KnownDLLs32, for x64 processes – KnownDLLs. For x64 processes, ws2_32.dllis is located in C:Windowssystem32.

– We create a section with the ws2_32.dll name in this directory. It looks as follows.

C
OBJECT_ATTRIBUTES sectionAttr;
        UNICODE_STRING normalize_section_name;
        RtlInitUnicodeString(&normalize_section_name, m_sectionPath.c_str());
        InitializeObjectAttributes( &sectionAttr, &normalize_section_name,
            0, NULL, NULL);        
    
        stat = NtCreateSection(&hsec,
                SECTION_ALL_ACCESS,
                &sectionAttr,
                NULL,
                PAGE_EXECUTE_READWRITE,
                SEC_IMAGE,
                hfile);

m_sectionPath equals to “KnownDlls32ws2_32.dll”.

– Finally, we increment the reference counter for our section using NtMakePermanentObject, and pass there the section handle. Itโ€™s important, or the object manager will delete our section.

To remove DLL hooks, we delete the section:

C
UNICODE_STRING normalize_section_name;
        RtlInitUnicodeString(&normalize_section_name, m_sectionPath.c_str());
        if(!(hsec = OpenSection(normalize_section_name))) 
        {
            throw std::runtime_error("DLL section name to delete does not exist!");
        }
        NTSTATUS stat = NtMakeTemporaryObject(hsec);

Unhooking

Then just delete symbolic link to native ws2_32.dll by DeleteFile and restart computer. As ws2_32.dll is always present in the sections, and weโ€™ve replaced it with the custom one, we should restart to put the native DLL back. Surely, we could simple create the section for the native DLL in the same way and not restart.

Hook Testing

The project is built, and now we start

PowerShell
HookInstall.exe โ€“install

Thus we install our server, it will run at the system start. Then we must start the service, we can do it for example typing

PowerShell
SC START HookInstall 

or calling HookInstall.exe without parameters. At the service start, the section is created and then the service is terminated.

First, letโ€™s see the results of our work using for example the utility from sysinternals named WinObj (you can also use other utilities for object view, like WinObjEx).

Section creation result in WinObj

Section creation result in WinObj.

Pay attention that ws2_32.dll is written in lower case while Windows at the system start creates the section with the name written in upper case.

Now letโ€™s test the work of our code. We start a browser โ€“ I used Mozilla Firefox โ€“ go to the search, for example yandex.com, and type Wrong_code. Enjoy the result.

Results of hook

Results of hook

You can also test this sample for other applications that use sockets, for example, sending or receiving the substituted text in ICQ.

The sample can be also easily improved, for example, by adding the filtration of text change for the specified IP addresses.

Conclusion

In this article, one of the numerous techniques of process intrusion was considered. Letโ€™s list some of the most famous approaches for hooking:

  • Using debugging Tools for Windows;
  • Buffers overloading;
  • Executable module patching directly on the disk;
  • Hooking of the Windows procedures using SetWindowHook and SetWindowLong;
  • Rewriting of the process original DLL with the custom one on the disk;
  • Intrusion using the legal Microsoft functions like โ€œblock startingโ€;
  • Loading via the AppInit_DLLs registry hive;
  • Loading using manifest;
  • Creation of the remote threadโ€ฆ

For example, take a look at another DLL hooking article in our Blog.

There are intrusion methods for user mode in this list, kernel mode hooking was not considered. But there are a lot of tricks with kernel mode.

To protect the process from intrusion via KnownDlls you can use kernel mode hook of API function named NtCreateSection checking the path to DLL. If it doesn’t correspond to the system one, the section creation must be blocked.

Download Sources (ZIP, 700KB)

Like our work? Learn more about our System Management technologies.

Continue reading with another Dev Blog article: How to reverse engineer iOS software.

Have a question?

Ask our expert!

Tell us about
your project

...And our team will:

  • Process your request within 1-2 business days.
  • Get back to you with an offer based on your project's scope and requirements.
  • Set a call to discuss your future project in detail and finalize the offer.
  • Sign a contract with you to start working on your project.

Do not have any specific task for us in mind but our skills seem interesting? Get a quick Apriorit intro to better understand our team capabilities.