Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >mov fs:[0],esp的含义

mov fs:[0],esp的含义

作者头像
战神伽罗
发布于 2019-07-24 00:44:24
发布于 2019-07-24 00:44:24
2.7K00
代码可运行
举报
运行总次数:0
代码可运行

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

SEH结构为链表,fs:[0]指向表头

struct{

pointer:next;

pointer:handleFunction

}

FS寄存器指向当前活动线程的TEB结构(线程结构)

偏移 说明 000 指向SEH链指针 //fs:[0] 004 线程堆栈顶部 //fs:[4] 008 线程堆栈底部 00C SubSystemTib 010 FiberData 014 ArbitraryUserPointer 018 FS段寄存器在内存中的镜像地址 020 进程PID 024 线程ID 02C 指向线程局部存储指针 030 PEB结构地址(进程结构) 034 上个错误号 得到KERNEL32.DLL基址的方法 assume fs:nothing ;打开FS寄存器 mov eax,fs:[30h] ;得到PEB结构地址 mov eax,[eax + 0ch] ;得到PEB_LDR_DATA结构地址 mov esi,[eax + 1ch] ;InInitializationOrderModuleList lodsd ;得到KERNEL32.DLL所在LDR_MODULE结构的InInitializationOrderModuleList地址 mov edx,[eax + 8h] ;得到BaseAddress,既Kernel32.dll基址

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
lodsb指令,将esi指向的地址处的数据取出来赋给AL寄存器,esi=esi+1;
lodsw指令则取得是一个字。
lodsd指令,取得是双字节,即mov eax,[esi],esi=esi+4;

stosb指令,将AL寄存器的值取出来赋给edi所指向的地址处。mov [edi]AL;edi=edi+1;
stosw指令去的是一个字。
stosd指令,取得是双字节,mov [edi],eax;edi=edi+4

代码运行在RING0(系统地址空间)和RING3(用户地址空间)时,FS段寄存器分别指向GDT(全局描述符表)中不同段:在RING3下,FS段值是0x3B(这是WindowsXP下值;在Windows2000下值为0x38。差别就是在XP下RPL=3);运行在RING0下时,FS段寄存器值是0x30。下面以XP为例说说。

一. RING3下的FS

当代码运行在Ring3下时,FS值为指向的段是GDT中的0x38段(RPL为3)。该段的长度为4K,基地址为当前线程的线程环境块(TEB),所以该段也被称为“TEB段”。

WINXPSP1及以前的Windows2000等系统中,进程环境块(PEB)的地址固定为0X7FFDF000,该进程的第一个线程的TEB地址为0X7FFDE000,第二个TEB的地址为0X7FFDD000…..但是自从WindowsXP SP2开始PEB和TEB的地址都是随机映射的(详见博文:MiCreatePebOrTeb函数注释)。

下图是WindowsXP SP3下的TEB结构(大小为0XFB8):

nt!_TEB +0x000 NtTib : _NT_TIB +0x000 ExceptionList : Ptr32 +0x004 StackBase : Ptr32 +0x008 StackLimit : Ptr32 +0x00c SubSystemTib : Ptr32 +0x010 FiberData : Ptr32 +0x010 Version : Uint4B +0x014 ArbitraryUserPointer : Ptr32 +0x018 Self : Ptr32 <—— +0x01c EnvironmentPointer : Ptr32 +0x020 ClientId : _CLIENT_ID +0x000 UniqueProcess : Ptr32 +0x004 UniqueThread : Ptr32 +0x028 ActiveRpcHandle : Ptr32 +0x02c ThreadLocalStoragePointer : Ptr32 +0x030 ProcessEnvironmentBlock : Ptr32 <—— +0x034 LastErrorValue : Uint4B +0x038 CountOfOwnedCriticalSections : Uint4B +0x03c CsrClientThread : Ptr32 +0x040 Win32ThreadInfo : Ptr32 ……

FS:[0X18]就是TEB所在的地址;FS:[0X30]就是PEB所在的地址。由于每个线程的TEB不尽相同,所以GDT中0X30描述符的基地址会随着线程的切换而改变的。我们来看看在什么地方变换的.看XP SP2 下的SwapContext的代码(该段代码在博文 pjf获得SwapContext地址方法的解析 中曾被引用,来说明如何获取SwapContext地址):

………… 8086dd6c 8b4b40 mov ecx,dword ptr [ebx+40h] 8086dd6f 894104 mov dword ptr [ecx+4],eax 8086dd72 8b6628 mov esp,dword ptr [esi+28h] 8086dd75 8b4620 mov eax,dword ptr [esi+20h]//这两条指令将新线程的TEB保存在KPRC 8086dd78 894318 mov dword ptr [ebx+18h],eax //的0X18中 8086dd7b fb sti 8086dd7c 8b4744 mov eax,dword ptr [edi+44h] 8086dd7f 3b4644 cmp eax,dword ptr [esi+44h] 8086dd82 c6475000 mov byte ptr [edi+50h],0 8086dd86 7440 je nt!SwapContext+0xe8 (8086ddc8) 8086dd88 8b7e44 mov edi,dword ptr [esi+44h] 8086dd8b 8b4b48 mov ecx,dword ptr [ebx+48h] 8086dd8e 314834 xor dword ptr [eax+34h],ecx 8086dd91 314f34 xor dword ptr [edi+34h],ecx 8086dd94 66f74720ffff test word ptr [edi+20h],0FFFFh 8086dd9a 7571 jne nt!SwapContext+0x12d (8086de0d) 8086dd9c 33c0 xor eax,eax 8086dd9e 0f00d0 lldt ax 8086dda1 8d8b40050000 lea ecx,[ebx+540h] 8086dda7 e850afffff call nt!KeReleaseQueuedSpinLockFromDpcLevel (80868cfc) 8086ddac 33c0 xor eax,eax 8086ddae 8ee8 mov gs,ax 8086ddb0 8b4718 mov eax,dword ptr [edi+18h] 8086ddb3 8b6b40 mov ebp,dword ptr [ebx+40h] 8086ddb6 8b4f30 mov ecx,dword ptr [edi+30h] 8086ddb9 89451c mov dword ptr [ebp+1Ch],eax 8086ddbc 0f22d8 mov cr3,eax 8086ddbf 66894d66 mov word ptr [ebp+66h],cx 8086ddc3 eb0e jmp nt!SwapContext+0xf3 (8086ddd3) 8086ddc5 8d4900 lea ecx,[ecx] 8086ddc8 8d8b40050000 lea ecx,[ebx+540h] 8086ddce e829afffff call nt!KeReleaseQueuedSpinLockFromDpcLevel (80868cfc) 8086ddd3 8b4318 mov eax,dword ptr [ebx+18h]//这几句就是将新线程的TEB的地址 8086ddd6 8b4b3c mov ecx,dword ptr [ebx+3Ch]//更新到GDT的0X38描述符的基地址 8086ddd9 6689413a mov word ptr [ecx+3Ah],ax //中去。 8086dddd c1e810 shr eax,10h // 8086dde0 88413c mov byte ptr [ecx+3Ch],al // 8086dde3 88613f mov byte ptr [ecx+3Fh],ah // 8086dde6 ff464c inc dword ptr [esi+4Ch] .........

二. RING0下的FS

当线程运行在Ring0下时, FS指向的段是GDT中的0x30段。该段的长度也为4K,基地址为0xFFDFF000(我的P4单核XPSP3下除了0FFDFF000外还会有其它值,不是是何原因?)。该地址指向系统的处理器控制区域(KPCR)。这个区域中保存这处理器相关的一些重要数据值,如GDT、IDT表的值等等(关于通过KPCR获得系统一些重要变量可看博文WindowsXP内核变量)。下面就是WindowsXP sp3中的KPCR数据结构

nt!_KPCR +0x000 NtTib : _NT_TIB +0x000 ExceptionList : Ptr32 +0x004 StackBase : Ptr32 +0x008 StackLimit : Ptr32 +0x00c SubSystemTib : Ptr32 +0x010 FiberData : Ptr32 +0x010 Version : Uint4B +0x014 ArbitraryUserPointer : Ptr32 +0x018 Self : Ptr32 <---- +0x01c SelfPcr : Ptr32 <----- +0x020 Prcb : Ptr32 +0x024 Irql : UChar +0x028 IRR : Uint4B +0x02c IrrActive : Uint4B +0x030 IDR : Uint4B +0x034 KdVersionBlock : Ptr32 +0x038 IDT : Ptr32 +0x03c GDT : Ptr32 +0x040 TSS : Ptr32 +0x044 MajorVersion : Uint2B ……………

看两个地址0x18和0x1C。在TEB中0x18指向自己,即TEB。而KPCR中指向自己的确是0x1C;0x18却是指向当前线程的TEB,所以0x18字段名叫做Self-used比较确切(WIN2K源码如此定义)。总之,不管是在RING3还是RING0,FS:[0x18]总是指向当前线程的TEB。

三. RING0与RING3之间的变换

RING0和RING3之间的变换通常是发生在系统调用与返回时,关于系统调用,可参看博文WINDOWS系统调用和 SYSENTER系统服务调用过程。

FS在RING0和RING3中是不同的值,在KiFastCallEntry / KiSystemService中FS值由0x3B变成0x30;在KiSystemCallExit / KiSystemCallExitBranch / KiSystemCallExit2中再将RING3的FS恢复。

下面来看看KiSystemService的开头部分代码(KiFastCallEntry也是一样):

nt!KiSystemService: 808696a1 6a00 push 0 808696a3 55 push ebp 808696a4 53 push ebx 808696a5 56 push esi 808696a6 57 push edi 808696a7 0fa0 push fs ;旧的RING3下的FS保存入栈 808696a9 bb30000000 mov ebx,30h 808696ae 668ee3 mov fs,bx ;FS=0X30 FS值变成了0X30. 808696b1 64ff3500000000 push dword ptr fs:[0] 808696b8 64c70500000000ffffffff mov dword ptr fs:[0],0FFFFFFFFh 808696c3 648b3524010000 mov esi,dword ptr fs:[124h] ;ESI=_ETHEAD 808696ca ffb640010000 push dword ptr [esi+140h] ;PreviousMode 808696d0 83ec48 sub esp,48h ; 808696d3 8b5c246c mov ebx,dword ptr [esp+6Ch] ; …………

再看看下面的KiSystemCallExit部分代码:

………… 80869945 8d6550 lea esp,[ebp+50h] 80869948 0fa1 pop fs //恢复FS值 8086994a 8d6554 lea esp,[ebp+54h] 8086994d 5f pop edi 8086994e 5e pop esi 8086994f 5b pop ebx 80869950 5d pop ebp 80869951 66817c24088000 cmp word ptr [esp+8],80h …………

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

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

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
利用调用门实现特权级间跳转 -- 实战篇
上一篇文章中,我们详细介绍了操作系统特权级,以及利用调用门、TSS 实现不同特权级之间的跳转。 利用调用门实现特权级间跳转 — 原理篇
用户3147702
2022/06/27
7410
利用调用门实现特权级间跳转 -- 实战篇
浅谈FS段寄存器在用户层和内核层的使用
在R0和R3时,FS段寄存器分别指向GDT中的不同段:在R3下,FS段寄存器的值是0x3B,在R0下,FS段寄存器的值是0x30.分别用OD和Windbg在R3和R0下查看寄存器(XP3),下图:
战神伽罗
2019/07/24
2.9K0
SEH学习
程序会出现错误,如果到处用if(!fun())这样的形式来侦错的话,代码不好维护。
全栈程序员站长
2022/11/15
6360
C/C++ 通用ShellCode的编写与调用
首先,我们的ShellCode代码需要自定位,因为我们的代码并不是一个完整的EXE可执行程序,他没有导入表无法定位到当前系统中每个函数的虚拟地址,所以我们直接获取到Kernel32.dll的基地址,里面的GetProcAddr这个函数,获取的方式有很多,第一种是暴力搜索,第二种通过遍历进程的TEB结构来实现,我们使用第二种方式尝试,一旦获取到该函数,就可以动态的调用任何想要的函数了。
王瑞MVP
2022/12/28
1.1K0
C/C++ 通用ShellCode的编写与调用
C/C++ 编写并提取通用 ShellCode
简易 ShellCode 虽然可以正常被执行,但是还存在很多的问题,因为上次所编写的 ShellCode 采用了硬编址的方式来调用相应API函数的,那么就会存在一个很大的缺陷,如果操作系统的版本不统一就会存在调用函数失败甚至是软件卡死的现象,下面我们通过编写一些定位程序,让 ShellCode 能够动态定位我们所需要的API函数地址,从而解决上节课中 ShellCode 的通用性问题。
王瑞MVP
2022/12/28
5500
C/C++ 编写并提取通用 ShellCode
C/C++ 反调试与绕过手法
反调试技术,恶意代码会用它识别自身是否被调试,或者让调试器失效,给反病毒工程师们制造麻烦,拉长提取特征码的时间线,本章将具体总结常见的反调试基础的实现原理以及如何过掉这些反调试手段,从而让我们能够继续分析恶意代码。
王瑞MVP
2022/12/28
6400
C/C++ 反调试与绕过手法
初探栈溢出
treme Vulnerable Drive,是一个项目,故意设计包含多种漏洞的驱动程序,旨在帮助安全爱好者来提升他们在内核层面的漏洞利用能力。
红队蓝军
2022/07/06
8240
初探栈溢出
x32下逆向 PsSetCreateProcessNotifyRoutine 进程钩子
因自己工作,可能后面会写ark工具.所以周六周日没事就逆向了一下进程回调数组. 虽然资料很多.但是自己动手自己明白.总比别人给的好.
IBinary
2019/07/30
1.2K0
x32下逆向 PsSetCreateProcessNotifyRoutine 进程钩子
KeUserModeCallback用法详解(Ring0调用Ring3代码)
ring0调用ring3早已不是什么新鲜事,除了APC,我们知道还有KeUserModeCallback.其原型如下: 代码:
战神伽罗
2020/03/19
2.3K0
1.7 完善自定位ShellCode后门
在之前的文章中,我们实现了一个正向的匿名管道ShellCode后门,为了保证文章的简洁易懂并没有增加针对调用函数的动态定位功能,此类方法在更换系统后则由于地址变化导致我们的后门无法正常使用,接下来将实现通过PEB获取GetProcAddrees函数地址,并根据该函数实现所需其他函数的地址自定位功能,通过枚举内存导出表的方式自动实现定位所需函数的动态地址,从而实现后门的通用性。
王瑞MVP
2023/07/05
2380
1.7 完善自定位ShellCode后门
三、系统调用
1.在 Ring3 的代码调用了 sysenter 指令之后,CPU 会做出如下的操作:
zhang_derek
2022/09/21
1.1K0
三、系统调用
VMPROTECT处理异常3--seh4
ExceptionList,正好位于TEB的偏移0处,总是由[FS:0]指向的,这个结构是用来注册我们的_except_handler()即:异常处理程序
franket
2020/04/06
2.4K0
构建API调用框架绕过杀软hook
我们知道杀软在API函数的监控上一般有两种手段,一种是在3环直接通过挂钩到自己的函数判断是否调用了这个API,另外一种方式就是在0环去往SSDT表的路径上挂钩来判断进0环后的操作。那么我们如果不想杀软监控我们的行为,之前提过的内核重载是一种绕过的方式,但是内核重载的动静太大,这里我们就通过直接重写3环到0环的API,通过重写KiFastCallEntry来自己调用内核的函数,以达到规避杀软的效果。
红队蓝军
2022/04/26
1.2K1
构建API调用框架绕过杀软hook
关于新版本cuckoo hook位置变动的思考
最近跟队友更新monitor版本,发现新版本将Zwxxx的hook位置移到了ntxxx的位置,其实做的是同一件事,为什么要如此?带着疑问我用OD进行了追踪。
战神伽罗
2019/07/24
6060
多核环境下的hook探究
文章首发奇安信攻防社区:https://forum.butian.net/share/1361
红队蓝军
2022/03/22
1.1K0
多核环境下的hook探究
msf生成的shellcode分析--走,组团分析shellcode呀
最近分析漏洞用到msf生成的样本进行测试,其中用到payload选项为Windows/exec cmd="calc.exe"的这个payload,本着一定要知道利用代码是怎么运行的想法,开始对该shellcode的详细分析。
极安御信安全研究院
2022/05/10
2.9K0
msf生成的shellcode分析--走,组团分析shellcode呀
实战分页机制实现 -- 通过实际内存大小动态调整页表个数
上一篇文章中,我们详细讲解了 32 位保护模式下的分页机制,最终,我们将 4GB 的内存区域划分为了连续的 1023 个分页,页表保存在 4MB 的空间中。 详解操作系统分页机制与实战 但是我们的内存大小到底是多少呢?如果内存总共只要 8MB,那上面的分页程序执行完,光是页表就占用了 4MB,空间已经所剩无几,可见,按需使用内存,合理规划页表的大小是非常重要的,而这一切的前提是必须要搞清楚内存总共有多少。 本文我们就来通过一个程序获取计算机的内存信息。
用户3147702
2022/06/27
8690
实战分页机制实现 -- 通过实际内存大小动态调整页表个数
shellcode编写指南
linux的shellcode就不用说了,直接通过一个int 0x80系统调用,指定想调用的函数的系统调用号(syscall),传入调用函数的参数,即可,懂的都懂。
Gamma实验室
2021/03/10
1.6K0
shellcode编写指南
漏洞分析丨HEVD-0x1.StackOverflow[win7x86]
该环境提供了各种内核漏洞场景供学习,本次实验内容是BufferOverflowStack
极安御信安全研究院
2022/06/30
5700
漏洞分析丨HEVD-0x1.StackOverflow[win7x86]
多核环境下的hook探究
文章首发奇安信攻防社区:https://forum.butian.net/share/1361
红队蓝军
2022/05/17
9600
多核环境下的hook探究
相关推荐
利用调用门实现特权级间跳转 -- 实战篇
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验