Loading [MathJax]/jax/input/TeX/config.js
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >[ Windows 10 x64中的RFG(Return Flow Guard)技术研究 ]2

[ Windows 10 x64中的RFG(Return Flow Guard)技术研究 ]2

作者头像
franket
发布于 2022-06-29 08:45:17
发布于 2022-06-29 08:45:17
45600
代码可运行
举报
文章被收录于专栏:技术杂记技术杂记
运行总次数:0
代码可运行
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 当然fs指向的地址未必一定要被映射,但fs指向的地址 + rsp 所指向的地址一定是被映射的地址,因为这就是影子栈的真实内存区域。 

 另外,在x64位下,cs,ds,ss,es都是平坦模式,也就是基地址都是从0地址开始,gs,fs特殊一下。windows 在x64下使用gs这个段寄存器代
 替原来的fs段寄存器的功能,例如,gs:[30]指向TEB,不再是x86下面的fs:[30]指向TEB,对gs的操作一般通过swapgs指令把
 IA32_KERNEL_GS_BASE值装填到GS.base当中,从而得到内核的数据结构完成相应的服务例程,例如下面的操作。
 
.text:0000000140172A80 KiSystemService proc near	
..
.text:0000000140172A80
.text:0000000140172A80                 cmp     [rsp+arg_0], 23h
.text:0000000140172A86                 jz      KiSystemService32User
.text:0000000140172A8C                 swapgs    <******<
.text:0000000140172A8F                 mov     rcx, r10
.text:0000000140172A92                 sub     rsp, 8
... 
.text:0000000140172ABB                 lea     r11, KiSystemServiceUser
.text:0000000140172AC2                 jmp     r11
.text:0000000140172AC2 KiSystemService endp 

但fs并没有对应的swap fs指令,在此之前,fs段寄存器一直是被保留的。在设计RFG功能后被重新启用了,使其指向“影子栈”地址。 

在Insider Preview14986版本的下,windows10只针对少量应用程序开启了RFG功能,例如针对svchost.exe,但其中Edge是
不支持RFG功能的。虽然可以在函数开头看到这样的指令,但这时fs指向的线性地址是0。

 mov qword ptr fs:[rsp],rax fs:00000073`11dfb588=00007ffd5bb757b5

也就是说此时,fs指向值是0,那么“影子栈”同真实栈是重合的,而在Insider Preview 15002版本后Edge开启了RFG保护,fs指向了真实的“影子栈”地址。 

总之,攻击者不能在用户态通过控制fs段寄存器来伪造“影子栈”。当然构造出写操作mov fs:[...],eax指令并且也能获得控制权
执行的情况除外(在Edge浏览器中,这样做的前提是你需要绕过CFG),我们强调的是fs指向的地址是不能在用户态被操纵的。 

[0x02.2] 内核层面的分析

首先fs = 53的选择子的赋值来自于内核方面,我们可以在KiSystemStartup函数内部看到

PAGELK:00000001403B10C1                 assume ds:nothing
PAGELK:00000001403B10C1                 mov     es, ax
PAGELK:00000001403B10C4                 assume es:nothing
PAGELK:00000001403B10C4                 mov     ax, 53h
PAGELK:00000001403B10C8                 mov     fs, ax
PAGELK:00000001403B10CB                 assume fs:nothing
PAGELK:00000001403B10CB                 test    cs:VslVsmEnabled, 0FFh
PAGELK:00000001403B10D2                 jnz     short loc_1403B10D9

1: kd> r gdtr
gdtr=ffffd68137fd0fb0  
我们关心的是fs指向的具体地址,在x86下,我们可以通过选择子53,和gdtr的地址推算获得fs的具体的地址值。但windows x64 long模式
下fs,gs 并不是通过GDT来获得的,而是通过读/写模式寄存器MSR寄存器来读取和设置的。   

我们可以看到在内核KiSwapThreadControlStack中看到,
KiSwapThreadControlStack proc near
...
.text:00000001401726FE                 test    r8, r8
.text:0000000140172701                 jz      loc_14017285B
.text:0000000140172707                 mov     rcx, rsi
.text:000000014017270A                 mov     rdx, [rbp+0E8h+var_128]
.text:000000014017270E                 call    KiSwapThreadControlStackDispatch
.text:0000000140172713                 test    al, al
.text:0000000140172715                 jz      loc_14017285B
.text:000000014017271B                 mov     eax, [rsi+7A0h]
.text:0000000140172721                 mov     edx, [rsi+7A4h]
.text:0000000140172727                 mov     ecx, 0C0000100h  <<********<<
.text:000000014017272C                 wrmsr                    <<********<<
.text:000000014017272E                 cli
.text:000000014017272F                 test    [rbp+0E8h+arg_0], 1
.text:0000000140172736                 jz      loc_140172810
...

ecx = 0xc0000100 对应的是fs段寄存器,gs对应的是0xc0000101。由上面的代码我们可推测出
KiSwapThreadControlStackDispatch负责获得线程的“影子栈”区域的地址,其地址值存储在eax,edx中是一个64bit地址,
然后通过wrmsr来写入fs,此时fs指向的地址就是由edx,eax共同决定的一个64位地址值了。

此时的rsi实际上指向了当前要“切换”线程的ethread首地址,所以0x7A0对应一个叫做UserFsBase的64位地址值。

0: kd> dt _ethread fffff801c0a2da40
ntdll!_ETHREAD
   +0x000 Tcb              : _KTHREAD
   +0x5e8 CreateTime       : _LARGE_INTEGER 0x0
...
   +0x798 PicoContext      : (null) 
   +0x7a0 UserFsBase       : 0
   +0x7a8 UserGsBase       : 0
...

本文系转载,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文系转载,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
[ Windows 10 x64中的RFG(Return Flow Guard)技术研究 ]1
让我们梳理下一RFG防护的基本思路:1) 在每一个函数开始处,”读”取当前栈rsp里面的值到rax中,该值就是函数的返回地址,我们记作return_value2) 然后将rax里面值”写”入fs:[rsp] 偏移处 , 也就是保留函数返回地址return__value的值到”影子栈“Thread Control stack当中。3) 在函数即将结束的时候检测当前返回地址值return_value和fs:[rsp]中保存的是否一致,如果一致,函数正常返回,如果不一致将跳向 xxx!_guard_ss_verf
franket
2022/06/29
5330
[ Windows 10 x64中的RFG(Return Flow Guard)技术研究 ]3
如果开启RFG保护的进程UserFsBase就会指向一个具体的地址值,当然这个地址值并不代表该区域一定会被映射。3)“影子栈”的内存布局 在Win10 Insider Preview14986的版本中KiSwapThreadControlStack并没有被调用,直到15002版本后才被调用。我们可以通过分析一下14986和15002,15016这几个不同的版本看到“影子栈”在设计上的变化。在14986版本中,我们以分析svchost进程为例来说明“影子栈”指向的具体地址(Edge在该版本中没开启RFG
franket
2022/06/29
2750
[ Windows 10 x64中的RFG(Return Flow Guard)技术研究 ]7
我们可以看到,整个“影子栈”区域是一个以0x00007A00~00000000开始的reserved区域。想来这里面应该有一些trick影藏在其中,因为NtQueryVirtualMemory/VirtualQueryEx通过解析vadroot来获得当前进程的内存分配情况,如果vad里面存储的“影子栈”就是一个512G的整体区域,那么在内核中针对每一个线程为什么能区分出这些“影子栈”的边界。显然上述API获得的信息是不全面的。通过调试我们来探测出这个整体影子栈的内存布局情况。我们可以在nt!PspAllo
franket
2022/06/29
3690
[ Windows 10 x64中的RFG(Return Flow Guard)技术研究 ]8
我们可以控制参数2指向待测试的内存,让其拷贝到我们分配的一段内存空间,即参数1,如果成功说明待测试内存可读。否则说明不可读。当然这里面有一个风险,待测试的目标内存如果刚好是很长的一个串结尾。而参数1不够长则会出现问题。 同样,我们也可以是GlobalLock函数来达到一样的目的。这个函数也是可以bypass CFG保护的,而且只需要一个参数更方便控制。LPVOID WINAPI GlobalLock( _In_ HGLOBAL hMem); 还有就是我们如果可以在Edge中分配出512G的内存空
franket
2022/06/29
6201
[ Windows 10 x64中的RFG(Return Flow Guard)技术研究 ]4
Private Data是一个512G大小的内存空间,起始地址是0x0000180~00000000. 我们可以看见,其中有很多是Read/Write属性的内存区块。在这些区块间插入很多reserved属性的内存区域。仔细看可以发现,这些大小44k,48k的区域就是不同线程的“影子栈”的区域。这里我们可以看到MS采取的一个技巧就是存在一个大小是279G左右的reserved区域从180~00000000开始。其实这个reserved区域在每个进程它都大小不同,我把它叫做“随机的reserved”区域,目的就
franket
2022/06/29
4580
[ Windows 10 x64中的RFG(Return Flow Guard)技术研究 ]
[目录][0x01].背景介绍[0x02].防护层面分析[0x02.1].用户层面fs[0x02.2].内核层面fs[0x02.3].“影子栈”的内存布局 [0x03].突破RFG的可能性[0x04].致谢 [0x01] 背景介绍 RFG(Return Flow Guard)是在最近的windows10x64里面加入的最新的防护措施。用来防止对栈上返回地址的篡改。不同于/GS策略,RFG是由编译器和操作系统共同来支持的特性,防御强度远远
franket
2022/06/29
5690
[ Windows 10 x64中的RFG(Return Flow Guard)技术研究 ]5
但我们这样的猜测只能保证找到一个“影子栈”的边界,并不能知道你想要利用的漏洞具体的对应的那个一个线程。当然理想的情况下,你对所有的“影子栈”都进行修改,保证漏洞触发shellcode能够运行,然后进程非常“理想”的crash掉。 我们继续看一下14986之后的版本,从15002之后微软修改了“影子栈”内存区域。Edge也开始支持RFG保护了。整体上512G的“影子栈”内存区域变成了一个reserved的区域,而不是上述能看到的独立的具有边界的“影子栈”区域。这样导致用NtQueryVirtualMem
franket
2022/06/29
1910
C/C++ 编写并提取通用 ShellCode
简易 ShellCode 虽然可以正常被执行,但是还存在很多的问题,因为上次所编写的 ShellCode 采用了硬编址的方式来调用相应API函数的,那么就会存在一个很大的缺陷,如果操作系统的版本不统一就会存在调用函数失败甚至是软件卡死的现象,下面我们通过编写一些定位程序,让 ShellCode 能够动态定位我们所需要的API函数地址,从而解决上节课中 ShellCode 的通用性问题。
王 瑞
2022/12/28
5860
C/C++ 编写并提取通用 ShellCode
暴力搜索x64 ssdt 以及挂钩HOOK
IDA加载ntoskrnl.exe 微软符号,进入,jump by name,随便输入一个,比如zwwritefile,层层跟踪,进入,可以发现
战神伽罗
2019/07/24
2.5K0
浅谈FS段寄存器在用户层和内核层的使用
在R0和R3时,FS段寄存器分别指向GDT中的不同段:在R3下,FS段寄存器的值是0x3B,在R0下,FS段寄存器的值是0x30.分别用OD和Windbg在R3和R0下查看寄存器(XP3),下图:
战神伽罗
2019/07/24
3K0
Linux syscall过程分析(万字长文)
为了安全,Linux 中分为用户态和内核态两种运行状态。对于普通进程,平时都是运行在用户态下,仅拥有基本的运行能力。当进行一些敏感操作,比如说要打开文件(open)然后进行写入(write)、分配内存(malloc)时,就会切换到内核态。内核态进行相应的检查,如果通过了,则按照进程的要求执行相应的操作,分配相应的资源。这种机制被称为系统调用,用户态进程发起调用,切换到内核态,内核态完成,返回用户态继续执行,是用户态唯一主动切换到内核态的合法手段(exception 和 interrupt 是被动切换)。
秃头哥编程
2019/08/23
15.2K1
【CSAPP】BombLab
《CSAPP》是指计算机系统基础课程的经典教材《Computer Systems: A Programmer's Perspective》,由Randal E. Bryant和David R. O'Hallaron编写。该书的主要目标是帮助深入理解计算机系统的工作原理,包括硬件和软件的相互关系,其涵盖了计算机体系结构、汇编语言、操作系统、计算机网络等主题,旨在培养学生系统级编程和分析的能力。
SarPro
2024/02/20
3320
【CSAPP】BombLab
x64架构下Linux系统函数调用
push指令将数据压栈。具体就是将esp(stack pointer)寄存器减去压栈数据的大小,再将数据存储到esp寄存器所指向的地址。
Orlion
2024/09/02
3010
x64架构下Linux系统函数调用
mov fs:[0],esp的含义
lea eax,SEH1[ebp] ;自己的异常处理函数地址 push eax ;把该异常处理函数地址压栈 push fs:[0] ;fs:[0]指向的是TIB[Thread information Block]结构中的 ;EXCEPTION_REGISTRATION 结构 mov fs:[0],esp ;让fs:[0]指向一个新的EXCEPTION_REGISTRATION 结构(就像链表插入一个新节点) mov esi,0 ;这两行指令就是用来处罚这个异常处理函数被调用的代码 mov eax,[esi];make a error for SEH
战神伽罗
2019/07/24
2.7K0
#x64汇编第二讲,复习x86汇编指令格式,学习x64指令格式
在X86下,查看inter手册可以清楚的看到x86汇编的指令格式. 图标如下
IBinary
2019/05/25
1.7K0
x86架构与x64架构在函数于栈中调用过程的不同之处
1、x86架构 x86架构是intel开发的一种32位的指令集。8个32位通用寄存器 eax,ebx,ecx,edx,ebp,esp,esi,edi。
Elapse
2020/08/17
2K0
x86_64汇编调试程序初步
掌握此基础,就可以用来修改无源代码的程序等,比如希望jstatd在指定的端口上监听,而不是一个值为0的随机端口号,请参见《防火墙内JVisualVM连接jstatd解决方案》。
一见
2018/12/24
7700
漏洞分析丨HEVD-0x1.StackOverflow[win7x86]
该环境提供了各种内核漏洞场景供学习,本次实验内容是BufferOverflowStack
极安御信安全研究院
2022/06/30
5760
漏洞分析丨HEVD-0x1.StackOverflow[win7x86]
挂钩 NtCreateThreadEx 导致 0xC00000F2 问题
近期在研究和开发基于虚拟化的虚拟 HOOK 技术。在 Windows 7 x64 环境开发实测期间,发现针对 NtCreateThreadEx 函数的 HOOK 存在问题:该函数大部分情况下变得只返回 0xC00000F2 (STATUS_INVALID_PARAMETER_4) 第 4 个参数无效的状态码。这导致系统出现很多问题,大部分的新线程都无法成功创建。为了解决这个问题,在这篇文章中对问题进行追溯,查找到底是哪里导致的。
稻草小刀
2022/12/12
8020
挂钩 NtCreateThreadEx 导致 0xC00000F2 问题
golang源码分析:go 汇编
AT&T格式的汇编代码中所有寄存器名字前面都有一个%符号,rsp代码sp寄存器,里面存的是栈顶指针。
golangLeetcode
2022/08/02
9550
golang源码分析:go 汇编
相关推荐
[ Windows 10 x64中的RFG(Return Flow Guard)技术研究 ]1
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验