首页 > 其他分享 >再探堆栈欺骗之动态欺骗

再探堆栈欺骗之动态欺骗

时间:2024-06-08 19:34:24浏览次数:20  
标签:动态 函数 调用 参数 寄存器 堆栈 欺骗 我们

本文首发先知社区:https://xz.aliyun.com/t/14542
在上篇文章 https://xz.aliyun.com/t/14487 中,我们讨论了静态堆栈欺骗,那是关于 hook sleep ,在睡眠期间改变堆栈行为的欺骗,这篇文章我们来一起讨论一下主动欺骗,允许任意函数发起时的堆栈欺骗。
相关的基础知识在上篇文章已经介绍,并且给出了推荐阅读的链接,这里就不再多说,接下来让我们一起动起手来进行调试。

手动进行堆栈欺骗

我们先在 x64dbg 中手动进行堆栈欺骗,这对我们理解接下来的项目有很大的帮助。
我随便找了一个程序,我们的想法是在栈底再伪造相同的两帧,都是RtlUserThreadStart +0x28,因为这是我系统上常见的偏移量,你在下面的截图中也可以发现相同的帧。
image.png
我们首先找到这个函数
image.png
可以看到第一条指令是 sub rsp 78,这意味着它需要的帧栈大小为 0x78,注意这里是 16 进制,我们只需要在当前栈底向下移动 15 次(0x78=0x08*15 , 十六进制满 16 进 1),然后就可以在这个位置创建新栈了。
x64dbg 自动标注的范围也证实了我们的理论
image.png
我们把这个位置改成想要的帧栈
image.png
然后再次重复即可完成第二帧的伪造
image.png
此时在 Process Hacker 中查看(注意以管理员权限开启),可以看到两个帧栈已经成功伪造。
image.png

LoudSunRun

第一个要介绍的项目 https://github.com/susMdT/LoudSunRun,这是作者在学习另一个项目https://github.com/klezVirus/SilentMoonwalk 时的产物,由于原项目有点大,作者在这个项目较小的代码库、间接系统调用支持和多参数支持。
这个 Poc 实现了 pPrintf,NtAllocateVirtualMemory 的直接调用,以及pNtAllocateVirtualMemory 的间接系统调用,我们接下来看一下项目:

一个参数的 Printf 的调用

首先先获取 Printf 的地址,这是我们要去调用的函数,另外又获取了 BaseThreadInitThunk+0x14 和RtlUserThreadStart+0x21 的位置,这是我们要去伪造的栈帧,因为这是作者电脑上一个线程中常见的栈底,当然在不同 windows 版本下偏移量是不一样的,我的 windows 版本下偏移量是 RtlUserThreadStart+0x28。还有一个FindGadget 函数,这是为了帮助我们寻找一个 jmp [rbx] 小工具的,我们后面会讲到。
也许还有人注意到了 CalculateFunctionStackSizeWrapper 函数,这个函数是用来计算帧栈大小的,就像我们上面手动伪造 0x78,在当前栈底向下移动 15 次一样,这个函数是根据 UnwindOp 来进行计算的,想要深入理解的话可以阅读一下:https://codemachine.com/articles/x64_deep_dive.html
image.png
紧接着就来到了 Spoof 函数,这是最关键的函数,是我们的欺骗函数,这个函数的参数是可变的,但是 Spoof的前七个参数是相对固定的,前四个参数是我们想要去调用的函数的前四个参数,第五个参数是一个重要的结构体,里面存储着进程上下文,如果需要间接系统调用的 SSN 以及要伪造的栈帧,第六个参数是要调用的函数的地址,第七个参数用来指示是否还有别的参数,假如为 2 的话在 Spoof 里面就会想办法获取后面两个参数。
image.png
下面是第五个参数的结构体。
image.png
在这里我还想再多说一句 x64 下参数的传递,前四个参数是放在Rcx,Rdx,R8,R9四个寄存器中,后面的参数就要放在栈上了,如图(图源 Windows x64 调用约定 - 堆栈框架):

准备和调用阶段

ok,现在让我们进入汇编看看到底发生了什么,
首先是一些准备操作,先将栈上的参数分别给 rdi 和 rsi,rdi 就是我们前面提到的结构体,为了便于恢复所以要先将当前寄存器的值给存储起来,rsi 就是要调用的函数的地址。
在下图的最后一行我们将 rax 给到了 r12,而之前 pop rax 则将原始的返回值给到了 rax,这样 r12 就存储了函数的返回值,这是因为 rax 是易失性寄存器,而 r12 是非易失性寄存器,也就是说即使被别的函数调用,r12 也会被 push 保护起来,最后再 pop 出来。

在x64的调用约定中规定易失性寄存器RAX, RCX, RDX, R8, R9, R10, R11, XMM0-XMM5 为易失性寄存器,RBX, RBP, RDI, RSI, RSP, R12, R13, R14, R15, XMM6-XMM15为非易失性寄存器

image.png
这段代码是处理参数的准备工作,r11 和 r13 分别存储了需要额外处理的参数的个数和已经额外处理的参数的个数,通过比较这两个寄存器的值就可以处理完所有的额外的参数了。由于 printf 是不需要额外的参数的,所以我们之后再分析
image.png
下面是一个循环,和我们上面说的一样,比较两个寄存器的值来判断是否还需要处理,等下我们再说是如何处理的,先跟着代码调试image.png
然后栈上分配一块空间,200h,然后 push 0,将之前的帧栈截断,剩下的就是我们自己要伪造的操作了。
image.png
接下来就是在伪造栈帧了,通过上面手动伪造应该很容易可以理解
image.png
现在看一下我们的栈帧,成功伪造
image.png
接下来是为了跳转和 fixup 做准备的,syscall 的代码等下再讲。将返回地址,rbx 寄存器值,fixup 的值给到前面那个欺骗的结构体,然后将 fixup 的值给到 rbx,因为它也是个非易失性寄存器,最后 jmp11。
image.png
此时看一下堆栈,十分干净
image.png

返回阶段

然后就是返回阶段了,当执行完 printf 后会进入到我们前面找到 jmp [rbx] 小工具,而我们的 rbx 存的是 fixup 函数地址,所以就会跳转到 fixup 函数
image.png
下面是我们的fixup 函数,主要就是恢复帧栈和前面保存的寄存器工作,最后 jmp 回到我们最初保持的返回点。
image.png
恢复之后的栈帧又是正常的
image.png

多参数调用

我们看一下多参数是怎么处理的
先将 rsp+30h 存储到 r10 里面,这样 r10+0x08 就可以找到下一个参数
image.png
这里 r14 是为了获取额外参数应该在的位置的,是我们需要压入栈中的数据的偏移量,首先加上 200h,这是我们在栈上分配的假栈的空间,然后是加 8,这对应着 push 0 指令,然后再加上要伪造的三个帧栈大小,这样就到了我们要调用的函数的帧栈了,然后以此为基础,第一个参数在 r14+0x28 位置处,然后每个参数依次加 0x08 即可
image.png
下面上一张图帮助大家理解
image.png
我们先找到参数需要移动到的位置,然后再将 r10+0x08 的值给到相应位置就可以了,相应位置是通过 rsp-r14 的值计算出来的,r14 是我们上面说的偏移量
image.png

间接系统调用

这个实现起来就很简单了,我们 jmp 去的时候先将 ssn 号存到 rax,然后直接跳转到 syscall 指令就可以了
image.png
注意这里跳转的函数直接就是 syscall 指令,Poc 里面作者是手动找到 syscall 指令的
image.png
当然获取 syscall 指令可以自动化获取,这里不再展开

标签:动态,函数,调用,参数,寄存器,堆栈,欺骗,我们
From: https://www.cnblogs.com/fdxsec/p/18238666

相关文章

  • 三探堆栈欺骗之Custom Call Stacks
    本文首发阿里云先知社区:https://xz.aliyun.com/t/14592背景知识在之前的文章中,我们介绍了静态欺骗和动态欺骗堆栈,今天我们来一起学习一下另一种技术,它被它的作者称为CustomCallStacks,即自定义堆栈调用。关于堆栈欺骗的背景我们就不再说了,这里我们补充一下回调函数和windows......
  • hdu1087简单动态规划
    求最长子序列的和。dp[i]=max(dp[i],dp[j]+xx[i])。importjava.util.Arrays;importjava.util.Scanner;publicclassMain{publicstaticvoidmain(String[]args){//TODO自动生成的方法存根Scannersc=newScanner(System.in);......
  • Spring AOP(实现,动态原理)详解版
    SpringAOP1.什么是AOP?1.1引入AOP依赖1.2编写AOP程序2.SpringAOP核⼼概念2.1切点(Pointcut)2.2连接点(JoinPoint)2.3通知(Advice)2.4切⾯(Aspect)3.通知类型3.1顺序3.2切⾯优先级@Order3.3⾃定义注解@MyAspect4.SpringAOP原理5动态代理怎么实现5.1JDK动......
  • vsCode开发实战之 C语言动态文件通讯录项目
    引言本项目所用开发环境为vsCode+CMake,至于为何如此选择,我就不赘述了,希望这篇博客能对您的学习有帮助!看完记得点赞收藏哦!!!一.项目结构项目根目录下结构如图:二.CMake配置CMake文件配置如图:三.头文件contact.h四.主函数文件main.c五.接口函数文件contact.c ......
  • SpringBoot高手之路jdk动态代理
    文章目录JDK动态代理基于jdk的动态代理Aop底层就是基于动态代理实现的实现代码先写代理对象工具JDK动态代理基于jdk的动态代理业务需求通过动态代理技术,对service层的方法统计执行时间–创建代理对象Aop底层就是基于动态代理实现的jdk动态代理技术是基于接口......
  • 通过site 包加载egg 或者whl pcakge 包并动态调用模块方法
    以前简单说过通过sys.path进行egg文件模块的加载,实际上我们可以结合site以及.pth能力,实现灵活的加载处理,同时通过importlib进行动态加载,以下是一个简单说明加载配置通过site包,添加自定义目录,目录里边包含.pth配置目录结构.pth内容使用核心是通过site添加......
  • 计算机网络实验二:动态路由配置
    这个是pkt文件https://pan.quark.cn/s/5a80aa8a21f7发现复制不来图片把实验报告也放在夸克网盘大家自行下载https://pan.quark.cn/s/1d9ea9d31bea有兴趣的可以一点一点跟着做没兴趣的自行下载提交(手动狗头)实验报告里面有私货记得删除修改这个pkt文件我没有配置协......
  • C++全栈聊天项目(20) 聊天列表动态加载
    聊天列表动态加载如果要动态加载聊天列表内容,我们可以在列表的滚动区域捕获鼠标滑轮事件,并且在滚动到底部的时候我们发送一个加载聊天用户的信号boolChatUserList::eventFilter(QObject*watched,QEvent*event){//检查事件是否是鼠标悬浮进入或离开if(wat......
  • 【C语言】动态内存经典笔试题(上卷)
    前言本系列将详细讲解4道有关动态内存的经典笔试题,以助于加深对动态内存的理解。这些题目都非常经典,你可能随时会遇到它们,所以非常重要。本文讲解其中的前两题。第一题这个程序运行的结果是什么?voidGetMemory(char*p){p=(char*)malloc(100);}voidTest(......
  • HTML,CSS,JavaScript实例——3D骰子,跨纬度蠕虫,动态登录表单。
    文章目录一、3D筛子1.HTML2.CSS二、跨纬度蠕虫1.HTML2.CSS3.JS三、动态登录表单1.HTML2.CSS一、3D筛子1.HTML<!--ringdivstartshere--><divclass="ring"><istyle="--clr:#00ff0a;"></i><istyle="--clr:#ff0057;">&l......