DLL基础入门笔记

​ 大一时候写的笔记吧好像是。。。现在回首一看简直就是一坨,放博客里记录一下鼠鼠的阴暗成长史吧。(喜)

0x01

DLL(动态链接库)

1.一般步骤:将共享DLL文件感染掉,电脑啥的重启时,所有调用它的都会被感染掉。

2.注意事项:

​ DLL模块中包含各种导出函数,用于向外界提供服务,exe。

​ 一个DLL在内存中只有一个实例。

​ DLL实现了代码的封装性;编程->接口

​ DLL的编制与具体的编程语言及编译器无关。

​ DLL模块需要的堆栈内存都是从运行进程的堆栈中分配出来的。

​ DLL函数中的代码所创建的任何对象(包括变量)都归调用它的线程或进程所有。

Windows API基础速成

1.其中32位Windows操作系统的编程接口称为Win32 API

2.应用层编程都是32位的API 而内核层才会用到64位的

3.函数分类:

​ 基本服务(开关机)

​ 组件服务(打开任务管理器即可查看)

​ 用户界面服务(平时编辑的窗口)

​ 图形多媒体服务(听歌,看电影)

​ 消息和协作(发信息,复制,粘贴,剪贴等等)

​ 网络

​ web服务(网页)

通过系统文件函数的操作掌握学习方法

eg. DeleteFileA 是 DeleteFile的净化版本

1.可适当取用错误码的返回函数来判断程序的出错原因,对照官网api函数详解。

DLL文件的编写速成

①.创建DLL

​ 调用约定:

​ __stdcall(函数的调用约定 )

​ __cdel

②.

1
2
3
4
BOOL WINAPI DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)

​ 上面这则代码是核心的入口函数 DLL文件 动态链接库入口函数

​ DLL模块的句柄 handle

​ DLLMain函数被调用的原因

image-20231022134247321

​ 调用原因集合大全:

image-20231022134447172

​ 保留项,也就是Windows的保留参数 lpReserved

​ 保留参数:不是不使用的参数,而是Windows不想让我们知 道作用的参数

导出函数(相当于一个接口)

1.DLL文件提供接口,前提是DLL文件中有导出函数了才能让外部程序实现对于DLL的调用

2.如果声明没有dllimport或dllexport特性的函数,则该函数不能视为DLL接口的一部分。

应用程序与导出函数的静态通讯方法

1.静态库是指在我们的应用中,有一些公共代码是需要反复使用,就把这些代码编译为“库”文件,链接器将从库文件取得所需代码,复制到生成的可执行文件中的这种库。

2.#pragma comment(lib , “dll”) 注意:没有分号 (函数参数是 后面那个)

​ 注意事项:

​ extern “C” void 函数名(参数);注意有分号 //防止编译后函数名被篡改或者粉碎

​ __declspec(dllexport):声明导出函数,将该函数从本DLL开放提供给其他应用程序使用

​ 如果没有声明DLLimport或dllexport特性的函数,则该函数不被视为DLL接口的一部分。因此,函数的定义必须存在于该模块或同一程序程序的另一个模块中。若要使函数成为DLL接口的一部分,必须将其他模块中的函数的定义声明为 dllexport

​ 1.**编译的时候:**编译exe文件的时候必须将所注入的DLL文件复制到exe文件所在的解决方案目录中(如果不详写路径的话)

​ 2.**运行的时候:**不需要lib,只需要dll

复现成功!

image-20231022144642025

0x02

一.应用程序与DLL导出函数的“动态”通讯:

​ 静态调用:通过连接器将DLL函数的导出函数写进可执行文件。(见part1中的后面部分)

​ 动态调用:相对于前一种调用属于动态调用,不是在连接时完成的,而是在运行时候完成的。

二.动态调用的核心代码:

此处只需要将原本静态调用下编写的dll文件与新编写的动态调用所生成的exe放在同一文件下即可

动态调用的详细改写代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <windows.h>

typedef void (*PMessage_Box)();

int main()
{
//将指定模块
HMODULE hModule = LoadLibrary("firstDll.dll");

//判断句柄是否为空
if (hModule == NULL)
{
MessageBox(NULL, "句柄错误!", "标题:", MB_OK);
return -1;
}

//从指定的动态链接库(DLL)中检索出的函数 bbb 的地址
PMessage_Box pMsg = (PMessage_Box)GetProcAddress(hModule, "bbb");

//执行函数
pMsg();
}

实现截图如下:

image-20231110195143567

image-20231110202121768

实现成功

0x03

一.导出函数查看工具的使用:

​ 一键略过,说一下可记知识点:

​ X86 -> 32位

​ X64 -> 64位

​ RVA:RVA是相对虚拟地址(Relative Virtual Address)的缩写,顾名思义,它是一个“相对”地址,也可以说是“偏移量”,PE文件的各种数据结构中涉及到地址的字段大部分都是以RVA表示的。

​ 主要就是查看dll中函数导出的RVA地址

二.DLL注入工具使用

​ 使用注入工具的话需要对于原来的dll文件有一个修改

image-20231110211535157

三.进程和线程的概念:

​ 1.进程,线程,CPU之间的关系就好像车间,车间资源,工厂之间的关系。

​ 2.操作系统的设计可以归结为三点:

​ (1).以多进程形式,允许多个任务同时运行;

​ (2).以多线程形式,允许单个任务分成不同的部分运行;

​ (3).提供协调机制,一方面防止进程之间和线程之间产生冲突,另一方面允许进程之间和线程之间共享资源。

​ 3.远线程注入:创建一个远程的线程到目标的进程中去执行LoadLibraryA函数,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include "iostream"

#include <Windows.h>


void Inject(int pid)
{
//返回进程的句柄
HANDLE ProcessHandle = OpenProcess(PROCESS_ALL_ACCESS, NULL, pid);

//加载kernel32.dll模块
HMODULE hModule = GetModuleHandleA("kernel32.dll");

//获取 dll载入函数的地址
LPTHREAD_START_ROUTINE lpStarAddress = (LPTHREAD_START_ROUTINE)GetProcAddress( hModule,"LoadLibraayA");

//创建远程线程
CreateRemoteThread(ProcessHandle, NULL, 0, lpStarAddress, 内存地址, 0, NULL);

}

int main()
{



system("pause");
return 0;
}

​ 4.地址获取;

​ 5.模块擦除:

​ 在调用完一个dll之后,记得用FreeLibrary(“被调用模块的句柄”)擦除一下,如若要加载的应用中存在自动扫描dll,那么这个模块将会暴露,所以要擦除。

四.远程卸载dll模块实战

​ 废话不多说,直接上代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
void Unject(int pid)
{
//返回进程的句柄
HANDLE ProcessHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);

//创建远程线程 //FreeLibrary的地址 //要卸载的目标dll地址
HANDLE hThread = CreateRemoteThread(ProcessHandle, NULL, 0, (LPTHREAD_START_ROUTINE)0x76FC8E60, (LPVOID)0x67B30000, 0, NULL);

//等待线程事件,当调用这个函数时
// 当前线程会暂停执行,直到所指定的对象变为可用或者信号状态发生变化才会继续执行后面的代码
WaitForSingleObject(hThread, 2000);

//防止内存泄露
CloseHandle(hThread);
CloseHandle(ProcessHandle);

}