其他
为什么在ASLR机制下DLL文件在不同进程中加载的基址相同
一
DLL 注入实现
#include "pch.h"
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
MessageBoxA(NULL, "您的进程已被注入", "注入警告", NULL);
break;
case DLL_THREAD_ATTACH:
MessageBoxA(NULL, "您的进程已被注入", "注入警告", NULL);
break;
case DLL_THREAD_DETACH:
MessageBoxA(NULL, "您的进程已被注入", "注入警告", NULL);
break;
case DLL_PROCESS_DETACH:
MessageBoxA(NULL, "您的进程已被注入", "注入警告", NULL);
break;
}
return TRUE;
}
1.4 运行以下代码,将 Dll 文件注入到记事本进程中。
#include <stdio.h>
int main()
{
// 获取目标进程的句柄
HWND hWnd = FindWindow(NULL, L"无标题 - Notepad");
if (hWnd == NULL) {
printf("未找到目标进程\n");
return 1;
}
DWORD processId;
GetWindowThreadProcessId(hWnd, &processId);
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, processId);
if (hProcess == NULL) {
printf("无法打开目标进程\n");
return 1;
}
// 在目标进程中分配内存
LPVOID pRemoteBuffer = VirtualAllocEx(hProcess, NULL, MAX_PATH, MEM_COMMIT, PAGE_READWRITE);
if (pRemoteBuffer == NULL) {
printf("无法在目标进程中分配内存\n");
return 1;
}
// 将DLL路径写入目标进程
char dllPath[] = "E:\\Test\\InjectDll.dll";
if (!WriteProcessMemory(hProcess, pRemoteBuffer, dllPath, sizeof(dllPath), NULL)) {
printf("无法写入目标进程内存\n");
return 1;
}
// 获取LoadLibrary函数的地址
HMODULE hKernel32 = GetModuleHandle(L"kernel32.dll");
if (hKernel32 == NULL) {
printf("未找到kernel32.dll\n");
return 1;
}
FARPROC pLoadLibrary = GetProcAddress(hKernel32, "LoadLibraryA");
if (pLoadLibrary == NULL) {
printf("未找到LoadLibrary函数\n");
return 1;
}
// 在目标进程中调用LoadLibrary函数加载DLL
HANDLE hThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)pLoadLibrary, pRemoteBuffer, 0, NULL);
if (hThread == NULL) {
printf("无法在目标进程中创建远程线程\n");
return 1;
}
printf("DLL注入成功\n");
// 等待远程线程退出
WaitForSingleObject(hThread, INFINITE);
// 清理资源
CloseHandle(hThread);
VirtualFreeEx(hProcess, pRemoteBuffer, 0, MEM_RELEASE);
CloseHandle(hProcess);
return 0;
}
● 定位目标进程:使用Windows API函数(如FindWindow)或其他技术来获取目标进程的句柄或进程ID;
● 打开目标进程:使用OpenProcess函数打开目标进程,获取进程的句柄,以便后续操作;
● 在目标进程中分配内存:使用VirtualAllocEx函数在目标进程中分配一块内存,用于存储DLL路径或其他数据;
● 将DLL路径写入目标进程:使用WriteProcessMemory函数将DLL路径或其他数据写入目标进程的内存空间;
● 获取函数地址:获取所需函数(例如LoadLibrary)在目标进程所加载的模块中的地址,通常使用GetModuleHandle和GetProcAddress函数;
● 在目标进程中创建远程线程:使用CreateRemoteThread函数在目标进程中创建一个远程线程,该线程执行加载DLL的函数,并将DLL路径作为参数传递;
● 等待远程线程退出:使用WaitForSingleObject函数等待远程线程退出,确保注入操作完成;
● 清理资源:关闭句柄、释放内存等,以确保不会产生资源泄漏。
二
为什么在ASLR机制下DLL文件在不同进程中加载的基址相同
2.1 ASLR
● 堆地址
● 栈地址
● PE文件加载基址
● PEB和TEB地址
·
ALSR 机制能保证在每次系统启动后,系统DLL文件在进程中的加载基址不会是默认的地址0x10000000(EXE文件的默认加载基址是0x00400000),而是一个随机的地址。系统重启后这个加载基址会再次变化,ASLR的出现使得shellcode中的关键跳转只能在系统重启前,甚至只有程序的本次运行时才能执行,这使得exploit的难度大大增加。
·
在ALSR开启的状态下,DLL 注入依然能实现是因为DLL文件在不同进程中加载的基址虽然经过了随机化的处理,但系统DLL文件(如system32目录下的DLL)在各个进程中通常加载地址仍然是相同的,以保证不同进程能互相调用这些系统DLL提供的 API。
2.2 Copy-On-Write
·
Copy-On-Write机制触发并不会影响虚拟地址空间的映射关系。因此,在Copy-On-Write机制中,虚拟地址空间中DLL的加载基址不会发生变化。A 进程仍然可以通过原始的加载基址访问和调用DLL中的代码和数据。
·
当多个进程加载同一个 DLL 文件并且它们的加载基址保持相同时,可以更好地利用 Copy-On-Write 机制。这样可以实现代码和只读数据的共享,延迟数据的复制,并提高内存利用率和性能。如果 DLL 加载地址不一致,Copy-On-Write 无法共享内存页,每个进程都需要单独复制 DLL 的只读内存,失去了内存优化的效果。
·
这里稍微延申一下,并不是多个进程中相同的虚拟地址都能映射到相同的物理地址。前面已经提到,每个进程的虚拟地址和物理地址的映射关系由页表记录,页表由操作系统内核维护。当操作系统决定切换到一个新的进程时,它会保存当前进程的上下文(包括寄存器和其他相关状态),然后加载新进程的上下文。
·
在上下文切换过程中,操作系统会切换页表,即将当前进程的页表替换为新进程的页表。也就是说,不同进程中相同的虚拟地址是通过不同的页表映射到物理地址的,所以虚拟地址相同,物理地址不一定相同。在某些特定情况下,不同进程中的相同虚拟地址可能会映射到相同的物理地址,例如不同进程共享内存。
2.3 PE文件的加载机制
◆重定位类型(Relocation Type):指定了需要进行的重定位操作,例如相对地址的基址绝对化。
◆偏移量(Offset):指定了在文件中的位置,即需要进行重定位的虚拟地址相对于模块基址的偏移量。
◆VirtualAddress = ImageBase + Offset(偏移量)
1.dll注入:系统kernel32.dll为什么在每个进程中的基址相同https://blog.csdn.net/weixin_43742894/article/details/105879904
2.《0 day 安全》
3. 《Windows 内核原理与实现》
看雪ID:ZyOrca
https://bbs.kanxue.com/user-home-944427.htm
# 往期推荐
1、2023 SDC 议题回顾 | 芯片安全和无线电安全底层渗透技术
6、2023 SDC 议题回顾 | 深入 Android 可信应用漏洞挖掘
球分享
球点赞
球在看