博注:偶很少弄Windows的东西,偶尔因为RE了ntoskrnl.exe->ntoskrnl.c,发现里面很多__readfsdword(32),__readfsdword(292)之类的调用,因此Google了一下,下面是记录,不敢说原创,故都列出了原文地址。

1.What is the “FS”/“GS” register intended for?

http://stackoverflow.com/questions/10810203/what-is-the-fs-gs-register-intended-for

There is what they were intended for, and what they are used for by Windows and Linux.

The original intention behind the segment registers was to allow a program to access many different (large) segments of memory that were intended to be independent and part of a persistent virtual store. The idea was taken from the 1966 Multics operating system, that treated files as simply addressable memory segments. No BS "Open file, write record, close file", just "Store this value into that segment" with dirty page flushing.

Our current 2010 operating systems are a giant step backwards, which is why they are called "Eunuchs". You can only address your process space's single segment, giving a so-called "flat (IMHO dull) address space". The segment registers on the x86-32 machine can still be used for real segment registers, but nobody has bothered (Andy Grove, former Intel president, had a rather famous public fit last century when he figured out after all those Intel engineers spent energy and his money to implement this feature, that nobody was going to use it. Go, Andy!)

AMD in going to 64 bits decided they didn't care if they eliminated Multics as choice and so disabled the general capability of segment registers in 64 bit mode. There was still a need for threads to access thread local store, and an thread needed a pointer ... somewhere in the immediately accessible thread state (e.g, in the registers) ... to thread local store. Since Windows and Linux both used FS for this purpose in the 32 bit version, AMD decided to let the 64 bit segment registers (GS and FS) be used essentially only for this purpose (I think you can make them point anywhere in your process space; dunno if the application code can load them or not). Intel in their panic to not lose market share to AMD on 64 bits, and Andy being retired, decided to just copy AMD's scheme.

It would have been architecturally prettier IMHO to make each thread's memory map have an absolute virtual address (e.g, 0-FFF say) that was its thread local storage (no [segment] register pointer needed!); I did this in an 8 bit OS back in the 1970s and it was extremely handy, like having another big stack of registers to work in.

So, the segment registers are now kind of like your appendix. They serve a vestigial purpose. To our collective loss.

Those that don't know history aren't doomed to repeat it; they're doomed to doing something dumber.

2. x86 memory segmentation on Wikipedia

http://en.wikipedia.org/wiki/X86_memory_segmentation

The x86-64 architecture does not use segmentation in long mode (64-bit mode). Four of the segment registers: CS, SS, DS, and ES are forced to 0, and the limit to 264. The segment registers FS and GS can still have a nonzero base address. This allows operating systems to use these segments for special purposes.

For instance, Microsoft Windows on x86-64 uses the GS segment to point to the Thread Environment Block, a small data structure for each thread, which contains information about exception handling, thread-local variables, and other per-thread state. Similarly, the Linux kernel uses the GS segment to store per-CPU data.

3. Getting the current thread ID without a syscall? 

http://web.archiveorange.com/archive/v/nwbg9rB8BAtPXVDARyvU
I have an unusual requirement: I need to get the current thread ID in as few instructions as possible.  On Windows, I managed to come up with this glorious hack:

#ifdef WITH_INTRINSICS
#   ifdef MS_WINDOWS
#       include <intrin.h>
#       if defined(MS_WIN64)
#           pragma intrinsic(__readgsdword)
#           define _Py_get_current_process_id() (__readgsdword(0x40))
#           define _Py_get_current_thread_id()  (__readgsdword(0x48))
#       elif defined(MS_WIN32)
#           pragma intrinsic(__readfsdword)
#           define _Py_get_current_process_id() (__readfsdword(0x20))
#           define _Py_get_current_thread_id()  (__readfsdword(0x24))
#        endif

#   endif
#endif
That exploits the fact that Windows uses the FS/GS registers to store thread/process metadata.

4. What does the ntoskrnl RE reveals?

PKTHREAD __stdcall KeGetCurrentThread()
{
  return (PKTHREAD)__readfsdword(292);
}
int __cdecl PsGetCurrentProcess()
{
  return *(_DWORD *)(__readfsdword(292) + 128);
}

5. KPCR,KPRCB,ETHREAD,KTHREAD,EPROCESS,KPROCESS,TEB,PEB

http://hi.baidu.com/sudami/item/d1a5b235bb60bb342f20c459

KPCR(Kernel's Processor Control Region,内核进程控制区域)是一个不会随WINDOWS版本变动而改变的固定结构体,在它的末尾[偏移0x120]指向KPRCB结构。

nt!_KPCR
   +0x000 NtTib            : _NT_TIB
   +0x01c SelfPcr          : Ptr32 _KPCR
   +0x020 Prcb             : Ptr32 _KPRCB
   +0x024 Irql             : UChar
   +0x028 IRR              : Uint4B
   +0x02c IrrActive        : Uint4B
   +0x030 IDR              : Uint4B
   +0x034 KdVersionBlock   : Ptr32 Void
   +0x038 IDT              : Ptr32 _KIDTENTRY
   +0x03c GDT              : Ptr32 _KGDTENTRY
   +0x040 TSS              : Ptr32 _KTSS
   ...// 省略
   +0x120 PrcbData         : _KPRCB

KPRCB同样是一个不会随WINDOWS版本变动而改变的固定结构体。它包含有指向当前KTHREAD的指针,偏移值0x004。其实也就是知道了当前的ETHREAD基地址。[因为ETHREAD的第一项便是KTHREAD,ETHREAD在后面讨论,现在讨论进程相关] [通过 KeGetCurrentPrcb() 函数即可得到PKPRCB,具体参见WRK]

展开KTHREAD,其中的_KAPC_STATE结构中包含当前KPROCESS的地址

nt!_KTHREAD
   +0x000 Header           : _DISPATCHER_HEADER
   ...
   +0x034 ApcState         : _KAPC_STATE

+0x034 ApcState         : struct _KAPC_STATE, 5 elements, 0x18 bytes
      +0x000 ApcListHead      : [2] struct _LIST_ENTRY, 2 elements, 0x8 bytes
      +0x010 Process          : Ptr32 to struct _KPROCESS, 29 elements, 0x6c bytes
      +0x014 KernelApcInProgress : UChar
      +0x015 KernelApcPending : UChar
      +0x016 UserApcPending   : UChar

而EPROCESS的第一项正是KPROCESS。联想我们熟悉的断EPROCESS链表隐藏进程的手法。通过PsGetCurrentProcess得到的其实是当前KPROCESS的地址,而KPROCESS就是EPROCESS结构体的第一项,这样就得到了当前的EPROCESS。然后遍历整个链表。。。

---->>大致流程:PsGetCurrentProcess()函数---->_PsGetCurrentProcess()宏----->KeGetCurrentThread()函数

---->>具体细节:
#define _PsGetCurrentProcess() (CONTAINING_RECORD(((KeGetCurrentThread())->ApcState.Process),EPROCESS,Pcb))
// 很明显,KeGetCurrentThread()得到KTHREAD结构体,KTHREAD偏移0x034处的
// ApcState中process即为EPROCESS的第一项KPROCESS的地址。CONTAINING_RECORD宏
// 将此地址减去它在EPROCESS中的偏移值,得到当前EPROCESS的实际地址

FORCEINLINE
struct _KTHREAD *
NTAPI KeGetCurrentThread (VOID)
{
#if (_MSC_FULL_VER >= 13012035)
    return (struct _KTHREAD *) (ULONG_PTR) __readfsdword (FIELD_OFFSET (KPCR, PrcbData.CurrentThread));
#else
    __asm { mov eax, fs:[0] KPCR.PrcbData.CurrentThread }
#endif
}
// fs在用户模式下指向TEB结构,在内核模式下指向KPCR

关于KPROCESS。里面保存了一些有用的信息,我们来简单的瞅下。

nt!_KPROCESS
   +0x000 Header           : _DISPATCHER_HEADER
   +0x010 ProfileListHead : _LIST_ENTRY
   +0x018 DirectoryTableBase : [2] Uint4B    // 进程的页目录PDT [涉及内存管理知识]
   +0x020 LdtDescriptor    : _KGDTENTRY    // GDT的入口
   +0x028 Int21Descriptor : _KIDTENTRY     // IDT的入口
   +0x030 IopmOffset       : Uint2B
   +0x032 Iopl             : UChar
   +0x033 Unused           : UChar
   +0x034 ActiveProcessors : Uint4B
   +0x038 KernelTime       : Uint4B
   +0x03c UserTime         : Uint4B
   +0x040 ReadyListHead    : _LIST_ENTRY
   +0x048 SwapListEntry    : _SINGLE_LIST_ENTRY
   +0x04c VdmTrapcHandler : Ptr32 Void
   +0x050 ThreadListHead   : _LIST_ENTRY // 指向KTHREAD链
   +0x058 ProcessLock      : Uint4B
   +0x05c Affinity         : Uint4B
   +0x060 StackCount       : Uint2B
   +0x062 BasePriority     : Char
   +0x063 ThreadQuantum    : Char
   +0x064 AutoAlignment    : UChar
   +0x065 State            : UChar
   +0x066 ThreadSeed       : UChar
   +0x067 DisableBoost     : UChar
   +0x068 PowerState       : UChar
   +0x069 DisableQuantum   : UChar
   +0x06a IdealNode        : UChar
   +0x06b Flags            : _KEXECUTE_OPTIONS
   +0x06b ExecuteOptions   : UChar

PEB是很有用的东西,写shellcode、定位EPROCESS等都可以用到它。PEB在EPROCESS偏移0x1b0处

nt!_EPROCESS
   +0x000 Pcb              : _KPROCESS
   ...
   +0x084 UniqueProcessId : Ptr32 Void
   +0x088 ActiveProcessLinks : _LIST_ENTRY
   ...
   +0x160 PhysicalVadList : _LIST_ENTRY
   +0x168 PageDirectoryPte : _HARDWARE_PTE
   ...
   +0x1b0 Peb              : Ptr32 _PEB
   ...

---->>获得PEB的地址是非常简单的。可以通过EPROCESS的偏移,也可以用硬编码实现[不同进程的PEB高位都是一样的]
xor esi, esi                     ;   FS寄存器 -> TEB结构,TEB+0x30 -> PEB结构
mov esi, fs:[esi + 30H]   ;   而PEB中包含有_PEB_LDR_DATA。通过一系列的
mov eax, esi                 ;   偏移可以定位到Kernel32.dll基地址。。。
ret                           ;   呵呵,参看gz1X大虾的文章:WIN下获取kernel基址的shellcode探讨

当然可以直接用Windbg来查看当前的PEB的结构 lkd>dt _peb

lkd> !peb
PEB at 7ffdc000    //高位都是7ffd
    InheritedAddressSpace:    No
    ReadImageFileExecOptions: No
    BeingDebugged:            No
    ImageBaseAddress:         01000000
    Ldr                       00191e90
    Ldr.Initialized:          Yes
    Ldr.InInitializationOrderModuleList: 00191f28 . 00193330
    Ldr.InLoadOrderModuleList:           00191ec0 . 00193320
    Ldr.InMemoryOrderModuleList:         00191ec8 . 00193328
    ...// 省略

The use of FS/GS registers相关推荐

  1. gs_fs0文件是什么_“ FS” /“ GS”寄存器的用途是什么?

    慕的地10843 它们的目的是什么,以及Windows和Linux的用途是什么.段寄存器的最初目的是允许程序访问许多不同的(大)内存段,这些段旨在独立并且是持久性虚拟存储的一部分.这个想法来自1966 ...

  2. 寄存器由来 ES CS SS DS FS GS区别

    段寄存器百度百科原地址 cs是代码段寄存器 存放当前正在运行的程序代码所在段的段基址,表示当前使用的指令代码可以从该段寄存器指定的存储器段中取得,相应的偏移量则由IP提供. ds是数据段寄存器 当前程 ...

  3. gs_fs0文件是什么_汇编 - 什么是“FS”/“GS”寄存器?

    它们的目的是什么,以及它们用于Windows和Linux的用途. 段寄存器背后的初衷是允许程序访问许多不同(大)的内存段,这些内存段旨在独立并且是持久虚拟存储的一部分. 这个想法取自1966年的Mul ...

  4. KVM 虚拟化原理探究--启动过程及各部分虚拟化原理

    KVM 虚拟化原理探究- overview 标签(空格分隔): KVM 写在前面的话 本文不介绍kvm和qemu的基本安装操作,希望读者具有一定的KVM实践经验.同时希望借此系列博客,能够对KVM底层 ...

  5. BIOS-SMI Introduction

    了解SMI之前先了解一些基本的知识. 1.0  SYSTEM MANAGEMENT MODE OVERVIEW SMM是一个特定的操作模式,用于处理系统级的功能.比如:Power management ...

  6. bochs调试方法与指令详解

    bochs调试FAQ: 一 基本调试命令 1.       Q:如何从引导扇区开始设置断点? A: BIOS被载入内存中运行,其引导扇区的地址一般都是在0x7c00,因为在实模式下,逻辑地址和物理地址 ...

  7. asm 32 /64

    我使用NASM编写的,运行在32位windows和linux主机上,但后来需求增加了,需要在64位windows和linux上运行,windows自身有个wow(windows on windows) ...

  8. [翻译]Global Descriptor Table-GDT

    全局描述符表(GDT)是 Intel x86 系列处理器(从 80286 开始)所使用的一种数据结构,目的是为了在程序运行期间划分具有不同属性的内存区域,比如:可以运行.可写入等区域的起始地址与访问权 ...

  9. 配置EditPlus为汇编的编辑工具

    转自:http://blog.csdn.net/fatsandwich/archive/2008/02/29/2131697.aspx from:http://www.tongyi.net/devel ...

最新文章

  1. 图的遍历——DFS(邻接矩阵)
  2. 运维工程师是桥的护栏_桥梁专家:钢结构桥比混凝土桥易涡振 以后出现涡振可能性会提高...
  3. 微信小程序 子组件调用父组件方法
  4. Duilib教程-自动布局3-分隔条
  5. 【转】Web Reference和Service Reference的区别
  6. java docur,JavaDoc生成API详解
  7. git .gitignore file does not work
  8. ABP框架 - 授权
  9. 各种常见数据传输线端口(插头)的分类
  10. 360来硬拼,云盘免费用了!新注册就能获得36T容量!
  11. Windows11 出现桌面管理器dwm.exe占用内存过高的解决方法
  12. Java Web 开发后续(四)
  13. var foo = 11+2+1; console.log(foo); //1121 好多文章答案写错了,我发下给初学的朋友看到,以免一开始就学错了...
  14. 使用谷歌的浏览器全屏打开地址
  15. 用 Async 函数简化异步代码
  16. MongoDB4.2.0安装包并配上安装教程
  17. 【FXCG】波段操作的四个步骤
  18. DAY5-URDF优化 简单小练习 工具
  19. DELL液晶显示器如何进入工程模式
  20. echart 报表统计

热门文章

  1. 【无标题】通信软件开发及应用
  2. kubernetes 后端 ceph 存储 QOS 限制
  3. 应用文表述计算机应用技术,在应用文写作中,常用的叙述方法是?
  4. 五轴数控转台_有人说先学会三轴,再去搞四轴、五轴加工中心,这几种机床有何区别呢?...
  5. 电脑文件搜索索引关闭
  6. python简单的三元一次方程求解
  7. MOS管规格书参数解析
  8. Tensorflow--tutorial--建造神经网络(输入层,隐藏层,输出层)
  9. graphpad两组t检验_SCI科研写作:如何利用GraphPadprism 8.0进行成组t检验
  10. 导入excel数据到DB