任务段进行任务切换,Jmp Call指令实现任务切换

2024-03-17 09:58

本文主要是介绍任务段进行任务切换,Jmp Call指令实现任务切换,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

        任务段描述符的格式我们就不说了太基本了,它的Base指向TSS结构的地址,Limit设置为0x68就够了TSS结构的大小就是104个字节,我们之前讲过,通过中断门,调用们提权的时候 ring0的堆栈ESP和SS都是TSS提供的,TSS结构每个核只有一个和KPCR结构一样,每当线程切换的时候会把当前线程的ESP0写入TSS结构中(以后进行线程切换逆向的时候会找到具体的代码),所以我们所Ring3提权到Ring0得到ESP都是当前线程的Ring的栈顶。

       TSS是CPU设计的实现多任务的方式,但是操作系统没有使用TSS作为线程切换,而是自己实现的线程切换,自己设计了一个Trap_Frame结构作为线程切换用于保存寄存器环境的一块内存,每个线程有一个通过线程结构可以找到这个字段,而TSS32位下操作系统只使用ESP0,SS0这两个字段用于门提权的时候获得RIng0的栈,

   今天我们带领大家通过任务段的方式实现任务的切换,可以使用jmp指令和Call指令

我们之前讲过jmp指令有四种使用方式   这是白皮书卷2jmp指令四种不同的跳转方式原话

1.jmp近跳,就是我们平常使用的jmp 硬编码后面接4个字节,可以跳转的地址为4GB,也称为段内跳转

2.jmp短跳,跳转的范围只限于当前EIP +128到-127 硬编码后面跟一个1字节

3.jmp远跳,   也称为段间跳转(会修改cs寄存器和eip寄存器的值)格式位  jmp  0x23:0x12345678  

4.任务切换,就是使用TSS结构中的值替换到当前寄存器,当我们把TSS 中的cs设置成0环的代码段就能实现提权

所以jmp指令是可以提权的,jmp指令任务切换的格式和jmp远跳的格式完全相同,有一点含义不同

jmp  0x48:0x12345678  如果此时0x48指向的是一个代码段的段描述符,此时就是jmp远跳,如果此时0x48指向的是一个任务段描述符,则此时是进行任务切换(会把这个加载任务段描述符到tr寄存器),jmp的远跳不能提权,jmp任务切换可以,

听过一位大佬说过非常经典的一段话,所有的指令要么操作内存,要么修改寄存器,jmp远跳修改的是cs和eip,而任务切换只是把tss里那些寄存器一下子全都替换而已

首先试试提权jmp任务切换提权

#include "stdafx.h"
#include <Windows.h>
int   taolaoda=0;
char  Ret[6]={0};
void  _declspec(naked) print(){
#if 0    
    __asm{
        mov eax,0x66666666
        mov taolaoda,eax
        mov eax,eax
        mov ebx,ebx
        pushfd
        pop eax
        or  eax,0x4000
        push eax
        popfd
        iretd
    }
#else
    __asm{
        int 3
         mov eax,0x66666666
        mov taolaoda,eax
        jmp fword ptr Ret
    }
#endif
}
int main(int argc, char* argv[]){
    char Buf[6]={0};

    char arrr[0x1000]={0};
    WORD  tr;
    DWORD Cr3=0;
    DWORD Pro=0;
    /*在0x1000 0000开辟一页的内存权限为可读可写*/
    int* P=(int*)VirtualAlloc((void*)0x10000000,0x1000,MEM_RESERVE | MEM_COMMIT,PAGE_READWRITE);
    if(NULL==P){
        MessageBox(NULL,"开辟虚拟内存失败",0,0);
    }
    __asm{
        str tr                                                       //读取tr寄存器的值到tr变量中
    }
    *(WORD*)&Ret[4]=tr;
    *(WORD*)&Buf[4]=0x48;
    printf("请输入CR3:");
    scanf("%x",&Cr3);
    P[1]=((int)arrr+0x100);
    P[2]=0x10;    
    P[7]=Cr3;
    P[8]=(int)print;            //EIP
    P[0xA]=1;
    P[0xB]=2;
    P[0xC]=3;
    P[0xD]=4;
    P[0xE]=((int)arrr+0x200);            //ESP

    P[0x12]=0x23;                //ES
    P[0x13]=0x8;                //CS
    P[0x14]=0x10;                //SS
    P[0x15]=0x23;                //DS
    P[0x16]=0x30;                //FS
    P[0x19]=0x20ac0000;    

    __asm{
        mov eax,0x00000000
        mov ecx,0x11117777
        mov edx,0x33333333
        mov ebx,0x11111111
        mov edi,0x22222222
        mov esi,0x33333333
        mov ebp,0x44449999

        jmp  fword ptr Buf;
    }
    printf("%p\n",taolaoda);
    getchar();
    return 0;
}

在构造一个任务段描述符 Base=0X10000000  Limlit=0x68

 

此时查看80042000处的TSS存储的值0x28选择子对应的也是一个任务段描述符 ,有人会奇怪一个是eb,一个是e9

e9是因为这个TSS段没有使用,eb是因为这个段描述符加载到了tr寄存器中,正在别使用,等会我们切换任务后就能看到变化了

找到当前进程的进行Cr3

输入TSS的cr3字段,注意cr3非常的重要,(我们知道每一个进行都有4GB虚拟内存),而每个进程都有一个0x10000000虚拟地址,这些0x10000000对应的物理内存肯定不同,CPU如何知道我们要找就是哪一个进程的?就是根据Cr3寄存器中的值查找的

MMU(存储管理单元,会根据Cr3和虚拟地址自动拆分,找到对应的物理页),这里需要一丢丢页的知识就能理解

任务切换就是把当TSS中的寄存器全都替换了,那肯定得把自己切换前的环境保存起来了,否则(我环境不就丢了)

返回地址就是0x40FCD0

继续F11可以看到Windug断下来了可以看到,它把当前环境保存到了前一个TSS中了就是0x28处的那个TSS

看一个当前TSS我们自己构造的  Previous Task  Link前一个任务段链接,没有填充这里的话如果使用Jmp不会填充这个字段,但是如果使用Call会在这里填充上一个TSS段的选择子,Jmp任务切换和Call任务切换的返回方式不一样,Jmp是切换提权,然后Jmp又切换回去

可以看到在切换任务之前tr加载的是0x28处的任务段描述符,指向jmp切换任务后 tr加载了0x48处的任务段描述符,并且把b位置为1了表示正在使用这个任务段,而前一个任务段的TSS就是0x28的Base指向的TSS中保存了切换任务之前的环境,便于我们等会在切换任务把那个环境恢复切换回去,

可以看到使用jmp和call(切换任务的时候都会把切换前的环境保存到上一个TSS中(call的我就不贴出来了))把call和jmp任务切换不同的贴不来

 

 

call指令的任务切换,还是当前进程的找到Cr3

F11继续,可以看到Call切换任务的时候,Previous Task  Link存储的前一个链接

按下去立马就挂了(这不像是蓝屏错误信息都没有,像是重启)这就是call指令任务切换不同的地方

Call指令任务切换的返回指令是iretd指令,中断门的时候返回指令及时iretd,当时说的是iretd指令从堆栈中弹出5值给 eip  cs  elfag  esp   ss,其实iretd指令会怎么执行依赖于elfag寄存器的NT位如果NT位=1就会从当前TSS结构中的Previous Task  Link找到上一个TSS的选择子,通过选择子找到上一个TSS,直接过如果单步Call里面就挂了

如果直接过就ok


直接F5就过了

 

直接进入call直接F5过 也能过  Previous Task  Link,jmp,call任务切换的时候他们都将切换前的环境保存在了前一个TSS中,

jmp指令如果jmp切换回去,call指令iretd  如果单步会将eflag寄存器nt设置为0,这个一定要主要,否定一定挂如果nt=0,iretd会从堆栈弹出5个值给那些寄存器,堆栈根本没有东西所以

获得Cr3

可以看到Call指令填充了

 

为了让大家跟加了解任务切换,我们从A进程切换到B进程

texttest进程有一个楼函数print地址0x401005

我们将Cr3改成texttest进程的

一按下就蓝了,应该TSS是设置的堆栈在texttest进程的没挂物理页,这就需要全都设置一下,在试试一次

设置好重新设置好esp为exttest进程控制堆栈

可以看到把堆栈设置后就过了,Windug给了一个警告,我们切换的进程不是当前进程就是cr3和当前进程的cr3不一样了

环境也保存在了前一个TSS里面

没执行一条指令就弹出一个警告

然后返回的时候有挂了,一想我就知道了,TSS的base是0x10000000是TSS进程的,使用texttest的cr3是找不到另外一个进程TSS(读其他进程的空间就是通过那个进程Cr3去读的哦,比如readprocessmemory函数writeprocessmemory),texttest的cr3去访问直接就挂了,如果在texttest的相同位置也搞个tss,或者挂相同的物理页,原理就是这样子了,这就是切换任务,写完这篇帖子感觉理解更加的深刻了,需要追到的是这是CPU提供切换任务的方式,操作系统没有用,但是本质是一样,切换Cr3切换ESP,任务就切换了至于其他寄存器就是回答现场的问题了,比如eip啊,(后面逆向线程切换的时候会带家详细的了解),学习内核的话没有捷径可以走,只有做实验,其他的什么都是扯淡,什么都是扯淡,除了自己动手做,TSS结构里面写入的堆栈必须是一个局部变量开辟在堆栈里面的,我试过用VirtualAlloc开辟块内存,让ESP指向这里面,和全局数据内存当做堆栈,都是直接挂,我们想线程有两个堆栈,是有默认大小的,而局部变量内存时在堆栈开辟的,所有可定不会有问题,而VirtualAlloc和全局数据的地址很大了,可能就溢出了,所以直接挂,我这里可能蓝屏了50次才想到这个原因,所有除了实验之外没有捷径可以走,太多太多的坑了。

 

这篇关于任务段进行任务切换,Jmp Call指令实现任务切换的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/818607

相关文章

使用Sentinel自定义返回和实现区分来源方式

《使用Sentinel自定义返回和实现区分来源方式》:本文主要介绍使用Sentinel自定义返回和实现区分来源方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录Sentinel自定义返回和实现区分来源1. 自定义错误返回2. 实现区分来源总结Sentinel自定

Java进行文件格式校验的方案详解

《Java进行文件格式校验的方案详解》这篇文章主要为大家详细介绍了Java中进行文件格式校验的相关方案,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录一、背景异常现象原因排查用户的无心之过二、解决方案Magandroidic Number判断主流检测库对比Tika的使用区分zip

Java实现时间与字符串互相转换详解

《Java实现时间与字符串互相转换详解》这篇文章主要为大家详细介绍了Java中实现时间与字符串互相转换的相关方法,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录一、日期格式化为字符串(一)使用预定义格式(二)自定义格式二、字符串解析为日期(一)解析ISO格式字符串(二)解析自定义

opencv图像处理之指纹验证的实现

《opencv图像处理之指纹验证的实现》本文主要介绍了opencv图像处理之指纹验证的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学... 目录一、简介二、具体案例实现1. 图像显示函数2. 指纹验证函数3. 主函数4、运行结果三、总结一、

Java使用Curator进行ZooKeeper操作的详细教程

《Java使用Curator进行ZooKeeper操作的详细教程》ApacheCurator是一个基于ZooKeeper的Java客户端库,它极大地简化了使用ZooKeeper的开发工作,在分布式系统... 目录1、简述2、核心功能2.1 CuratorFramework2.2 Recipes3、示例实践3

Springboot处理跨域的实现方式(附Demo)

《Springboot处理跨域的实现方式(附Demo)》:本文主要介绍Springboot处理跨域的实现方式(附Demo),具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不... 目录Springboot处理跨域的方式1. 基本知识2. @CrossOrigin3. 全局跨域设置4.

Spring Boot 3.4.3 基于 Spring WebFlux 实现 SSE 功能(代码示例)

《SpringBoot3.4.3基于SpringWebFlux实现SSE功能(代码示例)》SpringBoot3.4.3结合SpringWebFlux实现SSE功能,为实时数据推送提供... 目录1. SSE 简介1.1 什么是 SSE?1.2 SSE 的优点1.3 适用场景2. Spring WebFlu

基于SpringBoot实现文件秒传功能

《基于SpringBoot实现文件秒传功能》在开发Web应用时,文件上传是一个常见需求,然而,当用户需要上传大文件或相同文件多次时,会造成带宽浪费和服务器存储冗余,此时可以使用文件秒传技术通过识别重复... 目录前言文件秒传原理代码实现1. 创建项目基础结构2. 创建上传存储代码3. 创建Result类4.

SpringBoot日志配置SLF4J和Logback的方法实现

《SpringBoot日志配置SLF4J和Logback的方法实现》日志记录是不可或缺的一部分,本文主要介绍了SpringBoot日志配置SLF4J和Logback的方法实现,文中通过示例代码介绍的非... 目录一、前言二、案例一:初识日志三、案例二:使用Lombok输出日志四、案例三:配置Logback一

Python如何使用__slots__实现节省内存和性能优化

《Python如何使用__slots__实现节省内存和性能优化》你有想过,一个小小的__slots__能让你的Python类内存消耗直接减半吗,没错,今天咱们要聊的就是这个让人眼前一亮的技巧,感兴趣的... 目录背景:内存吃得满满的类__slots__:你的内存管理小助手举个大概的例子:看看效果如何?1.