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 删除。

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

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
WSAEventSelect模型 ---应用实例,重写TCP服务器实例
// WSAEvent.cpp : 定义控制台应用程序的入口点。 // #include "stdafx.h" #include <winsock2.h> #include <stdio.h> #pragma comment(lib,"WS2_32") class CInitSock { public: CInitSock(BYTE minorVer=2,BYTE majorVer=2) { WSADATA wsaData; WORD sock
用户1154259
2018/01/17
1.1K0
c++ 网络编程(十)TCP/IP LINUX/windows 异步通知I/O模型与重叠I/O模型 附带示例代码
原文链接:https://www.cnblogs.com/DOMLX/p/9662931.html
徐飞机
2018/09/30
1.6K0
c++ 网络编程(十)TCP/IP LINUX/windows    异步通知I/O模型与重叠I/O模型  附带示例代码
WinSock WSAEventSelect 模型
在前面我们说了WSAAsyncSelect 模型,它相比于select模型来说提供了这样一种机制:当发生对应的IO通知时会立即通知操作系统,并调用对应的处理函数,它解决了调用send和 recv的时机问题,但是它有一个明显的缺点,就是它必须依赖窗口。对此WinSock 提供了另一种模型 WSAEventSelect
Masimaro
2018/08/31
1.2K0
【网络编程】异步选择模型
核心:消息队列,操作系统为每个窗口创建一个消息队列,并且维护,我们想要使用消息队列,那就要创建一个窗口。
半生瓜的blog
2023/05/12
3910
【网络编程】select模型
监视socket集合,如果某个socket发生事件,(链接或者收发数据),通过返回值以及参数告诉我们。
半生瓜的blog
2023/05/12
2840
WSAEventSelect模型
WSAEventSelect模型,允许应用程序在一个或者多个套接字上接受基于时间的网络通知,也接受FD_XXX类型的网络事件,依靠windows的消息驱动机制和事件对象关联起来。 基本思路:为感兴趣的一组网络事件创建个事件对象,调用WSAEventSelect函数将网络事件和事件对象关联起来. winsock创建事件对象的函数WSACreateEvent,定义: WSAEVENT WSACreateEvent(void);//返回一个手工设置的事件对象句柄 创建以后,调用WSAEventSelect函数指定
用户1154259
2018/01/17
7320
WinSock 重叠IO模型
title: WinSock 重叠IO模型 tags: [WinSock 模型, 网络编程, 重叠IO模型] date: 2018-06-29 20:26:13 categories: Windows 网络编程 keywords: WinSock 模型, 网络编程, 重叠IO模型 --- 之前介绍的WSAAsyncSelect和WSAEvent模型解决了收发数据的时机问题,但是网卡这种设备相比于CPU和内存来说仍然是慢速设备,而调用send和recv进行数据收发操作仍然是同步的操作,即使我们能够在恰当的时机调用对应的函数进行收发操作,但是仍然需要快速的CPU等待慢速的网卡。这样仍然存在等待的问题,这篇博文介绍的重叠IO模型将解决这个等待的问题
Masimaro
2018/08/31
1.9K0
【网络编程】基于TCP/IP协议的C/S模型
全称——Transmission Control Protocol / Internet Protocol
半生瓜的blog
2023/05/12
6890
【网络编程】基于TCP/IP协议的C/S模型
开启服务和停止服务
Start函数用于开启服务 1 初始化状态变量 2 创建监听套接字 3 加载使用扩展API函数 4 创建完成端口对象 5 建立监听套接字和完成端口对象间的关联 6 为监听套接字注册FD_ACCEPT时间 7 投递AcceptEx IO不够时可以得到通知后创建监听线程 BOOL CIOCOPServer::Start(int nPort,int nMaxConnnections,int nMaxFreeBuffers,int nMaxFreeContexts,int nInitialReads) {
用户1154259
2018/01/17
1.9K0
很幽默的讲解六种Socket I/O模型
信息来源:幻影论坛     作  者: flyinwuhan (制怒·三思而后行)
ternturing
2018/09/12
8420
很幽默的讲解六种Socket I/O模型
WinSock Socket 池
之前在WinSock2.0 API 中说到,像DisConnectEx 函数这样,它具有回收SOCKET的功能,而像AcceptEx这样的函数,它不会自己在内部创建新的SOCKET,需要外部传入SOCKET作为传输数据用的SOCEKT,使用这两个函数,我们可以做到,事先创建大量的SOCKET,然后使用AcceptEx函数从创建的SOCKET中选择一个作为连接用的SOCKET,在不用这个SOCKET的时候使用DisConnectEx回收。这样的功能就是一个SOCKET池的功能。
Masimaro
2018/08/31
1.3K0
Windows下网络编程(win32API+VS2022)
下载地址:https://visualstudio.microsoft.com/zh-hans/downloads/
DS小龙哥
2024/05/24
2840
Windows下网络编程(win32API+VS2022)
IOCP使用acceptEX进行异步接收
代码部分疑惑说明 说明:①WSAAcceptEx函数作用是投递accept操作到完成端口内核,只有该函数可以完成此功能
全栈程序员站长
2022/11/11
7910
完成端口与线程池的关系_端口触发
关于IOCP网上到处都是资料,说的也很详细。我在这里就不再多说了,这只是本人在学习IOCP时的笔记,和配合AcceptEx写的一个极小的服务端程序。由于刚刚接触ICOP加上本人刚毕业不到一年,所以里面的理解或观点可能有误,还请大家多多批评!
全栈程序员站长
2022/11/11
9880
完成端口与线程池的关系_端口触发
C++ 实现的Ping类的封装
Ping 使用 Internet 控制消息协议(ICMP)来测试主机之间的连接。当用户发送一个 ping 请求时,则对应的发送一个 ICMP Echo 请求消息到目标主机,并等待目标主机回复一个 ICMP Echo 回应消息。如果目标主机接收到请求并且网络连接正常,则会返回一个回应消息,表示主机之间的网络连接是正常的。如果目标主机没有收到请求消息或网络连接不正常,则不会有回应消息返回。
王瑞MVP
2023/12/06
5730
C++ 实现的Ping类的封装
c++ 网络编程(一)TCP/IP 入门级客户端与服务端交互代码
原文地址:https://www.cnblogs.com/DOMLX/p/9601511.html
徐飞机
2018/09/30
2.1K0
c++ 网络编程(一)TCP/IP  入门级客户端与服务端交互代码
WSAAsyncSelect模型
WSAAsyncSelect模型允许程序以windows消息的形式接受网络事件通知 WSAAsyncSelect函数自动把套接字设为非阻塞模式,并且为套接字绑定一个窗口句柄,当有网络事件发生时,便向这个窗口发送消息 int WSAAsyncSelect( SOCKET s, //套接字句柄 HWND hWnd,//指定一个窗口句柄 u_int wMsg,//网络事件到来的消息ID long lEvent//指定那些需要发送 ); FD_READ:接收对方发送的数据包 FD_W
用户1154259
2018/01/17
7850
WSAAsyncSelect 消息模型
select 模型虽然可以管理多个socket,但是它涉及到一个时机的问题,select模型会针对所管理的数组中的每一个socket循环检测它管理是否在对应的数组中,从时间复杂度上来说它是O(n^2)的,而且还有可能发生数组中没有socket处于待决状态而导致本轮循环做无用功的情况,针对这些问题,winsock中有了新的模型——WSAAsyncSelect 消息模型 消息模型的核心是基于Windows窗口消息获得网络事件的通知,Windows窗口是用来与用户交互的,而它并不知道用户什么时候会操作窗口,所以Windows窗口本身就是基于消息的异步通知,网络事件本身也是一个通知消息,将二者结合起来可以很好的使socket通知像消息那样当触发通知时调用窗口过程。这样就解决了select中的时机问题和里面两层循环的问题 WSAAsyncSelect函数原型如下:
Masimaro
2018/08/31
6160
从零学习开源项目系列(四)LogServer源码探究
这是从零学习开源项目的第四篇,上一篇是《从零学习开源项目系列(三) CSBattleMgr服务源码研究》,这篇文章我们一起来学习LogServer,中文意思可能是“日志服务器”。那么这个日志服务器到底做了哪些工作呢?
范蠡
2018/07/25
7990
从零学习开源项目系列(四)LogServer源码探究
iocp详解_iocp是异步io吗
大家好,又见面了,我是你们的朋友全栈君。 #include “SOCKET.h” #include <Windows.h> DWORD WINAPI ThreadProc(LPVOID pvParam); #define PORT 8080 #define LISTEN_QUEUE 200 // AcceptEx 和 GetAcceptExSockaddrs 的函数指针,用于调用这两个扩展函数 LPFN_ACCEPTEX lpfnAcceptEx; LPFN_GETACCEPTEXSOCKADDRS lpfnGetAcceptExSockAddrs; void PostAcceptEx(IOCPHandle_s & listenHandle) { IO_DATA_s * p_io_data = new IO_DATA_s; p_io_data->socket = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP); listenHandle.Push(p_io_data); p_io_data->type = ACCEPT; lpfnAcceptEx(listenHandle.socket, p_io_data->socket, &p_io_data->addr, 0, 0, sizeof(SOCKADDR_IN) + 16, &p_io_data->len, &p_io_data->ol); } int main() { SocketInit(); IOCPHandle_s listenHandle; listenHandle.socket = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP); listenHandle.addr.sin_family = AF_INET; listenHandle.addr.sin_addr.S_un.S_addr = htonl(INADDR_ANY); listenHandle.addr.sin_port = htons(PORT); HANDLE IOCPhandle; IOCPhandle = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0 ); ::CreateThread(0, 0, ThreadProc, (void *)IOCPhandle, 0, 0); ::bind(listenHandle.socket, (SOCKADDR *)&listenHandle.addr, sizeof(SOCKADDR)); ::listen(listenHandle.socket, LISTEN_QUEUE); CreateIoCompletionPort((HANDLE)listenHandle.socket, IOCPhandle, (unsigned long)&listenHandle, 0); // 使用AcceptEx函数,因为这个是属于WinSock2规范之外的微软另外提供的扩展函数 // 所以需要额外获取一下函数的指针, // 获取AcceptEx函数指针 GUID GuidAcceptEx = WSAID_ACCEPTEX; GUID GuidGetAcceptExSockAddrs = WSAID_GETACCEPTEXSOCKADDRS; DWORD dwBytes = 0; WSAIoctl( listenHandle.socket, SIO_GET_EXTENSION_FUNCTION_POINTER, &GuidAcceptEx, sizeof(GuidAcceptEx), &lpfnAcceptEx, sizeof(lpfnAcceptEx), &dwBytes, NULL, NULL); // 获取GetAcceptExSockAddrs函数指针,也是同理 WSAIoctl( listenHandle.socket, SIO_GET_EXTENSION_FUNCTION_POINTER, &GuidGetAcceptExSockAddrs, sizeof(GuidGetAcceptExSockAddrs), &lpfnGetAcceptExSockAddrs, sizeof(lpfnGetAcceptExSockAddrs),
全栈程序员站长
2022/11/10
3810
相关推荐
WSAEventSelect模型 ---应用实例,重写TCP服务器实例
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验