首页 > 系统相关 >为什么在ASLR机制下DLL文件在不同进程中的加载基址相同?

为什么在ASLR机制下DLL文件在不同进程中的加载基址相同?

时间:2023-11-18 23:25:48浏览次数:43  
标签:文件 ASLR 基址 DLL 进程 NULL 加载

1. DLL 注入实现

以下是实现 DLL注入的简要步骤:

1.1 打开 Visual Studio,并创建一个新的 DLL 项目。

1.2 在"dllmain.cpp" 添加以下的代码

 1 // dllmain.cpp : 定义 DLL 应用程序的入口点。
 2 #include "pch.h"
 3 
 4 BOOL APIENTRY DllMain( HMODULE hModule,
 5                        DWORD  ul_reason_for_call,
 6                        LPVOID lpReserved
 7                      )
 8 {
 9     switch (ul_reason_for_call)
10     {
11     case DLL_PROCESS_ATTACH:
12         MessageBoxA(NULL, "您的进程已被注入", "注入警告", NULL);
13         break;
14     case DLL_THREAD_ATTACH:
15         MessageBoxA(NULL, "您的进程已被注入", "注入警告", NULL);
16         break;
17     case DLL_THREAD_DETACH:
18         MessageBoxA(NULL, "您的进程已被注入", "注入警告", NULL);
19         break;
20     case DLL_PROCESS_DETACH:
21         MessageBoxA(NULL, "您的进程已被注入", "注入警告", NULL);
22         break;
23     }
24     return TRUE;
25 }

1.3 生成 DLL 文件,得到一个名为 "InjectDll.dll" 的 DLL文件。

1.4 运行以下代码,将 DLL文件注入到记事本进程中

 1 #include <Windows.h>
 2 #include <stdio.h>
 3 
 4 int main()
 5 {
 6     // 获取目标进程的句柄
 7     HWND hWnd = FindWindow(NULL, L"无标题 - Notepad");
 8     if (hWnd == NULL) {
 9         printf("未找到目标进程\n");
10         return 1;
11     }
12 
13     DWORD processId;
14     GetWindowThreadProcessId(hWnd, &processId);
15     HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, processId);
16     if (hProcess == NULL) {
17         printf("无法打开目标进程\n");
18         return 1;
19     }
20 
21     // 在目标进程中分配内存
22     LPVOID pRemoteBuffer = VirtualAllocEx(hProcess, NULL, MAX_PATH, MEM_COMMIT, PAGE_READWRITE);
23     if (pRemoteBuffer == NULL) {
24         printf("无法在目标进程中分配内存\n");
25         return 1;
26     }
27 
28     // 将DLL路径写入目标进程
29     char dllPath[] = "E:\\Test\\InjectDll.dll";
30     if (!WriteProcessMemory(hProcess, pRemoteBuffer, dllPath, sizeof(dllPath), NULL)) {
31         printf("无法写入目标进程内存\n");
32         return 1;
33     }
34 
35     // 获取LoadLibrary函数的地址
36     HMODULE hKernel32 = GetModuleHandle(L"kernel32.dll");
37     if (hKernel32 == NULL) {
38         printf("未找到kernel32.dll\n");
39         return 1;
40     }
41 
42     FARPROC pLoadLibrary = GetProcAddress(hKernel32, "LoadLibraryA");
43     if (pLoadLibrary == NULL) {
44         printf("未找到LoadLibrary函数\n");
45         return 1;
46     }
47 
48     // 在目标进程中调用LoadLibrary函数加载DLL
49     HANDLE hThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)pLoadLibrary, pRemoteBuffer, 0, NULL);
50     if (hThread == NULL) {
51         printf("无法在目标进程中创建远程线程\n");
52         return 1;
53     }
54 
55     printf("DLL注入成功\n");
56 
57     // 等待远程线程退出
58     WaitForSingleObject(hThread, INFINITE);
59 
60     // 清理资源
61     CloseHandle(hThread);
62     VirtualFreeEx(hProcess, pRemoteBuffer, 0, MEM_RELEASE);
63     CloseHandle(hProcess);
64 
65     return 0;
66 }

总结一下 Dll 注入步骤

  • 定位目标进程:使用Windows API函数(如FindWindow)或其他技术来获取目标进程的句柄或进程ID;
  • 打开目标进程:使用OpenProcess函数打开目标进程,获取进程的句柄,以便后续操作;
  • 在目标进程中分配内存:使用VirtualAllocEx函数在目标进程中分配一块内存,用于存储DLL路径或其他数据;
  • 将DLL路径写入目标进程:使用WriteProcessMemory函数将DLL路径或其他数据写入目标进程的内存空间;
  • 获取函数地址:获取所需函数(例如LoadLibrary)在目标进程所加载的模块中的地址,通常使用GetModuleHandle和GetProcAddress函数;
  • 在目标进程中创建远程线程:使用CreateRemoteThread函数在目标进程中创建一个远程线程,该线程执行加载DLL的函数,并将DLL路径作为参数传递;
  • 等待远程线程退出:使用WaitForSingleObject函数等待远程线程退出,确保注入操作完成;
  • 清理资源:关闭句柄、释放内存等,以确保不会产生资源泄漏。

2. 为什么在ASLR机制下DLL文件在不同进程中加载的基址相同

2.1. ALSR

ASLR(Address Space Layout Randomization)是一种用于增加系统安全性的技术,它通过随机化内存地址的分配,使攻击者更难以利用已知的内存布局漏洞进行攻击。实际上ASLR的概念在Windows XP时代就已经提出来了,只不过XP上面的ASLR功能很有限,只是对PEB和TEB进行了简单的随机化处理,而对于模块的加载基址没有进行随机化处理,直到Windows Vista出现后,ASLR才真正开始发挥作用。

微软从Visual Studio 2005 SP1开始加入了/dynamicbase链接选项使编译好的程序支持随机基址,只需要在编译程序的时候启用/dynmicbase链接选项。(Visual Studio 2022 可以在项目属性中设置:配置属性——链接器——高级——随机基址)

 

PE文件中IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE值为1,则说明该 PE文件支持ASLR,如下图所示。

 

ALSR 会随机化的地址包括:

  • 堆地址
  • 栈地址
  • 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)。写时复制是现代操作系统的一个重要特性。操作系统使用页表(Page Table)来将进程的虚拟地址映射到物理地址。页表是一种数据结构,它存储了虚拟地址和物理地址之间的映射关系。

A 进程和 B 进程共享同一个 DLL 时,它们的虚拟地址空间中的虚拟地址会指向相同的物理内存页。这意味着它们共享同一份物理内存。当 A 进程尝试对 DLL 内存页进行写操作时,操作系统会触发写时复制,操作系统会将共享的物理内存页复制一份,创建一个新的物理页供 A 进程使用。A 进程会拥有自己的独立副本,而进程B仍然使用原始的物理内存页。

Copy-On-Write机制触发并不会影响虚拟地址空间的映射关系。因此,在Copy-On-Write机制中,虚拟地址空间中DLL的加载基址不会发生变化。进程A仍然可以通过原始的加载基址访问和调用DLL中的代码和数据。当多个进程加载同一个 DLL 文件并且它们的加载基址保持相同时,可以更好地利用 Copy-On-Write 机制。这样可以实现代码和只读数据的共享,延迟数据的复制,并提高内存利用率和性能。如果 DLL 加载地址不一致,Copy-On-Write 无法共享内存页,每个进程都需要单独复制 DLL 的只读内存,失去了内存优化的效果。

2.3. PE文件的加载机制

在 PE 文件中有一个加载基址(Image Base)的字段,它指定了 DLL 文件在内存中的起始地址。操作系统在加载DLL文件时,首先会检查文件头中的标志,确定是否启用了ASLR或者是否存在重定位表。如果存在重定位表,操作系统会遍历重定位表中的每个条目。重定位表中的每个条目包含了两个关键信息:

  • 重定位类型(Relocation Type): 指定了需要进行的重定位操作,例如相对地址的基址绝对化。
  • 偏移量(Offset): 指定了在文件中的位置,即需要进行重定位的虚拟地址相对于模块基址的偏移量。

操作系统使用以下公式计算PE 文件中需要进行重定位的虚拟地址:VirtualAddress = ImageBase + Offset(偏移量)

在相同的操作系统和相同的加载条件下,相同的DLL文件在不同进程中的重定位计算是一致的。因此,无论 ASLR 是否启用,PE 文件的加载机制决定了 DLL 文件在各个进程中的加载基址是相同的。

 

综合这三个角度,虽然ASLR在操作系统的进程管理中引入了加载基址的随机化,但由于Copy-On-Write和PE文件加载机制的作用,同一DLL文件在不同进程中的加载基址仍然是相同的。这有助于确保不同进程中的DLL文件内部结构保持一致,同时确保进程间的数据隔离,提高系统的整体安全性和资源使用效率。

标签:文件,ASLR,基址,DLL,进程,NULL,加载
From: https://www.cnblogs.com/zyorca/p/17841452.html

相关文章

  • Spring Cloud Gateway路由加载过程
    SpringCloud2021.0.5相关类先认识下相关的几个类RoutePredicateFactory,断言工厂,用于创建具体的断言。GatewayFilterFactory,过滤器工厂,用于创建具体的过滤器。Predicate,断言接口。GatewayFilter,过滤器接口。RouteDefinition,路由定义对象,在yml里配置的路由规则其实就是......
  • Angular 应用实现 Lazy Load(懒加载)的项目实战经验分享
    笔者之前两篇掘金社区文章,分别介绍了企业级Angular应用开启PWA特性和服务器端渲染,从而提升用户体验的两种设计思路:Angular应用支持PWA(ProgressiveWebApplication)特性的开发步骤分享基于AngularUniversal引擎进行服务器端渲染的前端应用StateTransfer故障排查......
  • 使用 ABAP + OLE 消费 Windows DLL 文件里的代码和服务
    在SAPABAP中,我们可以使用OLE(ObjectLinkingandEmbedding)技术来实现对WindowsDLL文件的代码和服务的消费。以下是一个详细的解决方案:首先,我们需要明确OLE技术在ABAP中的应用。OLE是由微软开发的一种技术,它允许对象(即应用程序功能)被嵌入到其他应用程序中。在ABA......
  • 十九——.NET 项目&DLL 反编译&未授权访问&配置调试报错
    .NET是微软推出的一种新的软件运行平台,它包括一整套应用范围广泛的技术,旨在迎接Java所带来的挑战。.NET给网站管理系统开发带来了很多新的技术,尤其是它的“混合语言”开发方式,是第一个支持多种语言混合开发的软件运行平台,下一代Windows--LongHorn的核心将会看到.NET的身影。如果读......
  • 动态加载——懒加载实现
    webpack是一个现代JavaScript应用程序的静态模块打包器(modulebundler)。当webpack处理应用程序时,它会递归地构建一个依赖关系图(dependencygraph),其中包含应用程序需要的每个模块,然后将所有这些模块打包成一个或多个bundle。前端工程化演进到今天,webpack做了很大的贡......
  • 前端页面的懒加载和预加载
    前言懒加载也就是延迟加载。当访问一个页面的时候,先把img元素或是其他元素的背景图片路径替换成一张大小为1*1px图片的路径(这样就只需请求一次,俗称占位图),只有当图片出现在浏览器的可视区域内时,才设置图片正真的路径,从而减轻服务器压力,避免用户等待时间过长(一般在网站图片很多......
  • 反编译C#的dll文件并修改,再重新生成dll
    1、把dll文件导入到ildasm工具中,ildasm是由微软提供的.net程序反编译工具,位于“C:\ProgramFiles\MicrosoftSDKs\Windows\v6.0A\bin” 2、在ildasm中File->dump,把dll文件转储为*.il文件存到某个指定文件夹里,得到*.il和*.res两个文件,有时也会有*.resource文件 3、打开得到的......
  • C#反编译dll文件,修改其中汉字内容,再重新生成dll
    一位电气朋友让我帮其反编译一个dll文件,只要修改dll中部分中文字符串的内容(比如:‘电机编号’、‘接线方式’等等)。这样的事情自己从来还没接触过,能否实现、难度怎样也没一个概念,但为了帮他一下,也为了挑战下看自己到底需要多久才能解决此全新问题,欣然接受了。从9:16到1......
  • JavaScript判断图片是否已经加载完毕的方法汇总_javascript技巧
    在网上有很多关于判断图片是否已经加载完毕的文章,但是有的浏览器并不适合,下面小编给大家分享一些有关JavaScript判断图片是否已经加载完毕方法汇总,具体内容如下所示:一.onload事件通过监听图片的onload事件,可以判断图片是否已经加载完成,兼容所有的浏览器(w3c推荐方法),代码示例如下......
  • @WebServiceClient wsdlLocation 动态给注解内容参数赋值
    动态给注解内容参数赋值@WebServiceClient(name="IXxxService",targetNamespace="http://xxx.xxx.xxx.com",wsdlLocation="${WSDL_URL}")publicclassIXxxServiceextendsService{ //静态变量在静态代码块加载后加载,且注解也在之后加载,完成动态注入修改注解里的参......